組込みシステム開発技術
____________________________________________________ 1.はじめに3
1.1概要______________________________________________________ 3
1.2設計内容__________________________________________________ 4
2.開発環境を設定__________________________________________ 4
インストール2.1Redhat ______________________________________________ 4
2.2取付アームのLinux GCCクロスコンパイラ_______________________________ 9
Linuxベース2.6.29-mini2440-20090708コア9をインストールしてコンパイルする2.3 ________________
3.キャラクタデバイスドライバの知識____________________________________ 9
3.1モジュール機構__________________________________________________ 9
3.2キャラクタデバイスを開発するための基本的な手順_____________________________________ 10
3.3メジャーとマイナー番号11 _______________________________________
3.4は_________________________________________ 12文字のドライバを実装します
4.ブザー原理_____________________________________________ 14
種類と働きブザー___________________________________ 14 4.1
分析ブザー4.2開発ボードの回路図_________________________________ 15
4.3GPB0パラメータ________________________________________________ 15
全体的なデザイン_______________________________________________ 16
5.1デザインのアイデア_________________________________________________ 16
5.2設計手順_________________________________________________ 16
6.ドライバ17及び試験手順________________________________________
6.1beep.c ____________________________________________________ 17
6.2beep_tset.c ________________________________________________ 21
7.経営成績及びスクリーンショット_________________________________________ 22
総合的なデザインの概要と考え方______________________________________ 25
1.はじめに
1.1概要
Linuxのドライブは、インタフェースを介して通信する、ハードウェア機能せずに上位層ソフトウェアが理解する場合には、本質的にソフトウェアプログラムであり、コンピュータのハードウェアドライバを提供します。
システムコールは、カーネルとアプリケーションとの間のインターフェースであり、ドライバは、カーネルおよびカーネルとハードウェアとの間のブリッジであるハードウェアとの間のインタフェースです。アプリケーションは、ハードウェアデバイス上で動作する通常のファイルと同様に動作することができ、それはアプリケーションのためのハードウェアの詳細を遮蔽するので、アプリケーションビューは、ハードウェアデバイスは、デバイスファイルです。
Linuxのドライバはカーネルの一部であり、システムは、デバイスコントローラおよび対応する機器を管理します。初期化と解放装置;にデータを送信し、ハードウェアからデータを読み取る、エラー検出及び処理装置が表示されます。これは主にので、いくつかの機能です。
一般的に、運転者は、装置の一つのタイプを管理することができます。例えば、それらが異なるUディスクの大容量記憶装置に属している、我々は、各U-ディスクのドライバを記述する必要はありませんが、唯一つのドライバは、これらの大容量記憶装置のすべてを管理することができます。
私たちは、異なるハードウェア、階層のカーネル抽象多くをサポートするために、ドライバーのさまざまなを追加しやすくするために、これらの階層は、上のLinuxデバイスドライバです。彼らは抽象的な様々なドライバインタフェース、ドライブだけで、簡単にカーネルに新しいドライバを追加することができ、適切なコールバック関数を記入する必要があります。
一般的に、三つのカテゴリーにLinuxドライバは、それは、ブロックデバイスドライバ、およびネットワークデバイスドライバのキャラクタデバイスドライバです。ライトキャッシュブロックデバイスがサポートしており、デバイスがランダムアクセスをブロックすることができなければなりません。ブロックデバイスドライバは、主に、磁気ディスク装置に使用されます。
そして、キャラクタデバイスなしキャッシュすることでI / O操作。キャラクタデバイスは、バイト単位で動作しているが、それは1つのバイトのみの操作を行うことができますと言うことではありません。たとえば、キャラクタデバイスは、我々はかつてのmmapを通じて大量のデータをやり取りすることができます。キャラクタデバイスは、比較的単純で柔軟です。
1.2設計内容
デザインはシンプルなキャラクタデバイスドライバの設計、ドライバの設計は、mini2440ブザーに基づいています。
2.開発環境を構築します
2.1Redhatインストール
仮想マシンを作成します。メニューバー[ファイル] - > [新規作成]> [仮想マシン]をクリックします。[次へ]をクリックします。
一般的なオプションを選択します。
LinuxのRed Hat Linuxの選択
仮想マシンストレージの名前と住所を記入してください。
ディスクサイズを選択
2.2取付アームのLinux GCCクロスコンパイラ
仮想マシンのルートディレクトリにアームのlinux-gccの-4.3.2.tgzをコピーします
ファイルを解凍します。tar zxvf腕のlinux-gccの-4.3.2.tgzを
在bash_profile里添加路径:gedit ~/.bash_profiel
路径 /root/usr/local/arm/4.3.2/bin
source ~/.bash_profile 使更改生效
2.3安装及编译linux-2.6.29-mini2440-20090708内核
复制内核到root目录下
解压内核文件 tar zxvf linux-2.6.29-mini2440-20090708.tgz
使内核文件生效:cp config_mini2440_n35 .config
使用make命令完成编译
3.字符设备驱动相关知识
3.1模块机制
Linux提供了机制被称为模块(Module)的机制
提供了对许多模块支持, 包括但不限于, 设备驱动
每个模块由目标代码组成( 没有连接成一个完整可执行程序 )
insmod 将模块动态加载到正在运行内核
rmmod 程序移除模块
Linux内核模块的程序结构
- module_init()---模块加载函数(必须)
通过insmod或modprobe命令加载内核模块时,模块的加载函数 会自动被内核执行,完成模块的相关初始化工作
- module_exit()---模块卸载函数(必须)
当通过rmmod命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块装载函数相反的功能
- MODULE_LICENSE()---模块许可证声明(必须)
模块许可证(LICENSE)声明描述内核模块的许可权限
如果不声明LICENSE,模块被加载时,将收到内核被污染(kernel tainted)的警告
- module_param()---模块参数(可选)
模块参数是模块被加载的时候可以被传递给它的值,它本身对应模块内部的全局变量。
- EXPORT_SYMBOL()---模块导出符号(可选)
内核模块可以导出符号(symbol,对应于函数或变量)到内核
其他模块可以使用本模块中的变量或函数
- 其他一些声明MODULE_XXXXX()---模块声明(可选)
模块加载函数
static int __init initialization_function(void)
{
/* 初始化代码 */
}
module_init(initialization_function);
模块卸载函数
static void __exit cleanup_function(void)
{
/* 释放资源 */
}
module_exit(cleanup_function);
3.2字符设备开发基本步骤
- 确定主设备号和次设备号
- 实现字符驱动程序
实现file_operations结构体
实现初始化函数,注册字符设备
实现销毁函数,释放字符设备
- 创建设备文件节点
3.3主设备号和次设备号
- 主设备号是内核识别一个设备的标识。
整数(占12bits),范围从0到4095,通常使用1到255
- 次设备号由内核使用,用于正确确定设备文件所指的设备。
整数(占20bits),范围从0到1048575,一般使用0到255
- 设备编号的内部表达
dev_t类型(32位):
用来保存设备编号(包括主设备号(12位)和次设备号(20位))
从dev_t获得主设备号和次设备号:
MAJOR(dev_t);
MINOR(dev_t);
将主设备号和次设备号转换成dev_t类型:
MKDEV(int major,int minor);
- 分配主设备号
手工分配主设备号:找一个内核没有使用的主设备号来使用。
#include <linux/fs.h>
int register_chrdev_region( dev_t first, unsigned int count, char *name );
- 动态分配主设备号:
#include <linux/fs.h>
int alloc_chrdev_resion(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
- 释放设备号
void unregister_chrdev_region(dev_t first, unsigned int count);
3.4实现字符驱动程序
- cdev 结构体
struct cdev
{
struct kobject kobj; /* 内嵌的kobject 对象 */
struct module *owner; /*所属模块*/
struct file_operations *ops; /*文件操作结构体*/
struct list_head list;
dev_t dev; /*设备号*/
unsigned int count;
};
- file_operations 结构体
字符驱动和内核的接口:
在include/linux/fs.h定义
字符驱动只要实现一个file_operations结构体
并注册到内核中,内核就有了操作此设备的能力。
- file_operations的主要成员:
struct module *owner: 指向模块自身
open:打开设备
release:关闭设备
read:从设备上读数据
write:向设备上写数据
ioctl:I/O控制函数
llseek:定位读写指针
mmap:映射设备空间到进程的地址空间
- ioctl函数
为设备驱动程序执行“命令”提供了一个特有的入口点
用来设置或者读取设备的属性信息。
int ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
- cmd 参数的定义
不推荐用0x1,0x2,0x3之类的值
Linux对ioctl()的cmd参数有特殊的定义
构造命令编号的宏:
_IO(type,nr)用于构造无参数的命令编号;
_IOR(type,nr,datatype)用于构造从驱动程序中读取数据的命令编号;
_IOW(type,nr,datatype)用于写入数据的命令;
_IOWR(type,nr,datatype)用于双向传输。
type和number位字段通过参数传入,而size位字段通过对datatype参数取sizeof获得。
- Ioctl函数模板
int xxx_ioctl( struct inode *inode, struct f ile *filp, unsigned int cmd,
unsigned long arg)
{
...
switch (cmd)
{
case XXX_CMD1:
...
break;
case XXX_CMD2:
...
break;
default: ///*不能支持的命令 */
return - ENOTTY;
}
return 0;
}
3.5字符设备驱动结构
4.蜂鸣器原理
4.1蜂鸣器的种类和工作原理
蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。
压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。有的压电式蜂鸣器外壳上还装有发光二极管。多谐振荡器由晶体管或集成电路构成。当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
有源蜂鸣器和无源蜂鸣器的区别:这个“源”字是不是指电源,而是指震荡源,即有源蜂鸣器内有振荡源而无源蜂鸣器内部没有振荡源。有振荡源的通电就可以发声,没有振荡源的需要脉冲信号驱动才能发声。
4.2开发板上蜂鸣器原理图分析
由原理图可以得知,蜂鸣器是通过GPB0 IO口使用PWM信号驱动工作的,而GPB0口是一个复用的IO口,要使用它得先把他设置成TOUT0 PWM输出模式。
4.3GPB0参数
5.总体设计
5.1设计思路
Linux设备驱动属于内核的一部分,Linux内核的一个模块可以以两种方式被编译和加载:
(1)直接编译进Linux内核,随同Linux启动时加载;
(2)编译成一个可加载和删除的模块,使用insmod加载(modprobe和insmod命令类似,但依赖于相关的配置文件),rmmod删除。这种方式控制了内核的大小,而模块一旦被插入内核,它就和内核其他部分一样。
这次的蜂鸣器驱动就采用动态模块加载的方式
5.2设计步骤
- 编写简单的字符设别驱动程序框架
- 编写控制蜂鸣器控制开关函数
- 编译模块,生成.ko
- 编写用户层测试程序
- 编译用户层测试程序,生成可执行程序beep_test
- 将生成的.ko 模块和应用层测试程序 beep_test 下载到目标板
- 用 insmod 装载模块
- 创建设备节点
mknod /dev/beep c 253 0
- 运行用户层测试程序 beep_test
#./beep_test
如果你的 beep_test 的属性不是可执行的,可以用 chmod 777 beep_test 将其设置成可执行
程序。
6.1beep.c
#include<linux/module.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/errno.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/io.h>
#include<asm/system.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include <asm/irq.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <mach/hardware.h>
#include <mach/gpio-fns.h>
#include <asm/irq.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <plat/devs.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/sysdev.h>
#include <linux/platform_device.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <plat/pm.h>
#define BEEP_MAJOR 201
#define BEEP_START_CMD 0x0
#define BEEP_STOP_CMD 0x1
static int beep_major=BEEP_MAJOR;
static struct cdev BeepDevs;
static int beep_open(struct inode *inode,struct file *filp)
{
return 0;
}
static int beep_relesae(struct inode *inode,struct file *filp)
{
return 0;
}
static void beep_stop(void)
{
s3c2410_gpio_cfgpin(S3C2410_GPB(0),S3C2410_GPIO_OUTPUT);
s3c2410_gpio_setpin(S3C2410_GPB(0),0);
printk("stop\n");
}
static void beep_start(void)
{
s3c2410_gpio_cfgpin(S3C2410_GPB(0),S3C2410_GPIO_OUTPUT);
s3c2410_gpio_setpin(S3C2410_GPB(0),1);
printk("start\n");
}
static int beep_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
switch(cmd)
{
case BEEP_START_CMD:
printk("beep_strat\n");
beep_start();
break;
case BEEP_STOP_CMD:
printk("beep_stop\n");
beep_stop();
break;
default:
printk("default\n");
break;
}
return 0;
}
static struct file_operations beep_remap_ops={
.owner = THIS_MODULE,
.open =beep_open,
.release = beep_relesae,
.ioctl = beep_ioctl,
};
static void beep_setup_cdev(struct cdev *dev, int minor,struct file_operations *fops)
{
int err,devno = MKDEV(beep_major,minor);
cdev_init(dev,fops);
dev->owner = THIS_MODULE;
dev->ops = fops;
err = cdev_add(dev,devno,1);
if(err)
{
printk("error %d adding beep %d \n",err,minor);
}
}
static int __init beep_init(void)
{
int result;
dev_t dev=MKDEV(beep_major,0);
if(beep_major)
{
result= register_chrdev_region(dev,1,"beep");
}
else
{
result=alloc_chrdev_region(&dev,0,1,"beep");
beep_major=MAJOR(dev);
}
if(result<0)
{
printk("beep: unable to get major %d \n",beep_major);
return result;
}
if(beep_major==0)
{
beep_major=result;
}
beep_setup_cdev(&BeepDevs,0,&beep_remap_ops);
printk("beep devices installed,with major %d",beep_major);
return 0;
}
static void __exit beep_exit()
{
cdev_del(&BeepDevs);
unregister_chrdev_region(MKDEV(beep_major,0),1);
printk("beep device uninstalled\n");
}
MODULE_AUTHOR("xdq");
MODULE_LICENSE("GPL");
module_init(beep_init);
module_exit(beep_exit);
6.2beep_tset.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int dev_fd;
char c;
dev_fd=open("/dev/beep",O_WRONLY|O_NONBLOCK);
if(dev_fd == -1)
{
printf("con not flie\n");
exit(0);
}
ioctl(dev_fd,0x0,0);
getchar();
getchar();
ioctl(dev_fd,0x1,0);
getchar();
getchar();
close(dev_fd);
return 0;
}
7.运行结果及截图
传输beep.ko,ls命令查看文件。
传输beep_test可执行文件。使用./beep_test命令运行测试程序
beep_stat,蜂鸣器响了,按下键盘一键,beep_stop,蜂鸣器停止