Linux中的ted工具,Linux下设备应用和驱动程序的编写(以RSSI为例)

作者:ARM混迹江湖

[摘自]http://blog.tianya.cn/blogger/post_show.asp?BlogID=648077&PostID=9434859Linux下所有的设备都是以文件的形式来操作的。设备软件分为两部分:应用程序和驱动程序,其中,应用程序负责打开设备并对它发出读写指令,驱动程序接受指令并完成读写的过程。

本文以AD5310用于AP/BG为例,讲述其应用和驱动的编写过程。

1. 硬件电路





 其中GPIO为CPU管脚,当J2上有Power Meter接上时,BNC_EN为低,程序开始从系统中获取RSSI,换算成相应的值以SPI时序送给AD5310,AD5310把它转化为相应电压,在Power Meter上显示。

2. 驱动实现

 2.1 代码编写

 驱动的编写主要是编写操作函数,例如读写等,因此驱动程序的结构比较单一,其主要的不同是操作函数的实现过程。

AD5310驱动的实现代码即说明如下:

#ifndef __KERNEL__ //判断驱动是编译进内核还是编译成模块

#define __KERNEL__

#endif

#ifndef MODULE

#define MODULE

#endif



#include … //包含相关头文件

#ifndef __MOD_INC_USE_COUNT

#define AH_MOD_INC_USE_COUNT(_m) \

 if (!try_module_get(_m)) { \

 printk(KERN_WARNING "try_module_get failed\n"); \

 return NULL; \

 }

#define AH_MOD_DEC_USE_COUNT(_m) module_put(_m)

#else

#define AH_MOD_INC_USE_COUNT(_m) MOD_INC_USE_COUNT

#define AH_MOD_DEC_USE_COUNT(_m) MOD_DEC_USE_COUNT

#endif

… //此处声明函数

struct file_operations spi_fops= //告诉系统应用程序的某个命令由谁来执行

{ //file_operation 是系统定义的结构体

 .owner = THIS_MODULE,

 .open = AD5310_open,

 .release = AD5310_release,

 .write = AD5310_write,

 .read = AD5310_read,

};

char spi_name[]="AD5310"; //device name

static int gmajor = 254; //device ID

/*************************************/

static int __init spidrv_init_module(void) //register device

{

int retv; //register_chrdev()是linux系统注册函数

 retv=register_chrdev(gmajor,spi_name,&spi_fops);

 if(retv<0)

 {

 printk("Register Fail!\n");

 return retv;

 }

 printk("SPI device OK!\n");

 return 0;

}



static void __exit spidrv_cleanup(void) //unregister device

{

 int retv;

 retv=unregister_chrdev(gmajor,spi_name);

 if(retv<0)

 {

 printk("UnRegister Fail!\n");

 return;

 }

 printk("SPIDRV:GOOD-bye!\n");

}



static int AD5310_open(struct inode *inode,struct file *file)

{ //打开文件时设置操作标记

 AH_MOD_INC_USE_COUNT(THIS_MODULE);

 return 0;

}



static int AD5310_release(struct inode *inode,struct file *file)

{

 AH_MOD_DEC_USE_COUNT(THIS_MODULE);

 return 0;

}

/*******************************************************************

 *function:init GPIO

 *description:GPIO7 use for DAC input,GPIO9 use for CS,GPIO12 use as

 *input,GPIO14 use as clock source

 *******************************************************************/

 void GPIO_init(void)

{

*IXP4XX_GPIO_GPOER = *IXP4XX_GPIO_GPOER & 0xBD7F;//GPIO7,GPIO9,GPIO14 ,output

}

/*************************************

 *read data from input GPIO12

 *************************************/

static ssize_t AD5310_read(struct file *fp, char __user *buf, size_t len, loff_t *loff)

{

 int input;

 printk("read the status of GPIO12\n");

 *IXP4XX_GPIO_GPOER = *IXP4XX_GPIO_GPOER | 0x1000;//GPIO12,input

 Delay(5);

 input = *IXP4XX_GPIO_GPINR; //get the status of GPIO12 pin

 input = input >> 12;

 input = input & 0x01;

 *buf = input;

 return(1);

}

/*********************************************************************

 *write data to output GPIO9

 *AD5310 input register contents:

 * DB15(MSB) ........................DB0(LSB)

 * x x PD1 PD0 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 x x

 *

 *PD1 PD0 = 0 0 normal operation

 *PD1 PD0 = 0 1 1K resister to GND power down modes

 *PD1 PD0 = 1 0 100K to GND power down modes

 *PD1 PD0 = 1 1 Three-State power down modes

 ********************************************************************/

static int AD5310_write(struct file *fp,char *buf,int len,loff_t *loff)

{

 int i;

 int temp;

 int spibuf;

 char *p;

 int writedata;



 p=buf; //get the data ,buf[1] low buf[0] high

 writedata=*p;

 p++;

 writedata = (writedata << 8) + *p;

 if(writedata > 1023 || writedata < 0)

{

printk("Err:the data must be 0~1023");

return -1;

}

 spibuf=0xC003; //set AD5310 operation mode as normal operation

 temp=writedata;

 temp= temp << 2;

 spibuf += temp; //the data packet needed to send to AD5310

 GPIO_init();

 Delay(100);

 *IXP4XX_GPIO_GPOUTR=*IXP4XX_GPIO_GPOUTR & 0xFDFF;//set GPIO9 low

 *IXP4XX_GPIO_GPOUTR=*IXP4XX_GPIO_GPOUTR | 0x4000;//set GPIO14 high

 for(i=0;i<16;i++) //start to send the data

{

*IXP4XX_GPIO_GPOUTR=*IXP4XX_GPIO_GPOUTR | 0x4000;//set GPIO14 high

 if ((spibuf & 0x8000) == 0)

 {

*IXP4XX_GPIO_GPOUTR=*IXP4XX_GPIO_GPOUTR & 0xFF7F; //GPIO7 output 0

}

else

 {

*IXP4XX_GPIO_GPOUTR=*IXP4XX_GPIO_GPOUTR | 0x80; //GPIO7 output 1

}

spibuf = spibuf <<1;

*IXP4XX_GPIO_GPOUTR=*IXP4XX_GPIO_GPOUTR & 0xBFFF; //set GPIO14 low

}

 *IXP4XX_GPIO_GPOUTR=*IXP4XX_GPIO_GPOUTR | 0x200;//set GPIO9 high

 *IXP4XX_GPIO_GPOUTR=*IXP4XX_GPIO_GPOUTR | 0x4000;//set GPIO14 high

 return 2;

}



/**************************************************************/

module_init(spidrv_init_module);

module_exit(spidrv_cleanup);



/*end of spidrv.c*/

 2.2 驱动编译

 在linux2.6内核下,驱动须编译成 .ko 的形式,即需要两个编译阶段,第一步生成 .o 文件,第二步生成 .ko 文件

 AD5310驱动的Makefile编写如下:

PWD := $(shell pwd) //指向当前目录

TOPDIR=$(PWD)/../

Tool := $(TOPDIR)/tools/buildroot/build_armeb/bin/ //交叉编译工具的路径

KERNELDIR=$(TOPDIR)/src/kernels/linux-2.6.13.2/ //系统内核路径

EXTRA_CFLAGS += -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -Wall//环境变量

ARC :=arm //CPU类型(平台类型)

CROSS_CC :=$(Tool)armeb-linux- //gcc名称

obj-m += spidrv.o //目标文件的源文件



default: //默认执行的命令及参量

 make ARCH=$(ARC) CROSS_COMPILE=$(CROSS_CC) -C $(KERNELDIR) SUBDIRS=$(PWD) modules

clean: //执行清除命令时清除的文件类型

 rm -rf *.o *.ko *mod.c



2.3 驱动加载

 把生成的 .ko 文件放到板子上的某个路径下如/lib/modules,执行如下命令:

 #insmod /lib/modules/spidrv.ko > /dev/null

 #mknod /dev/AD5310 c 254 1

此时若驱动正常工作,就会输出打印信息 SPI device OK! ,并且在/dev 目录下可以看到 AD5310 这个设备。

3. 应用实现

Linux下应用程序以文件的形式来操作设备,其最基本的操作顺序为

Open() -> read()/write() -> close()

RSSI应用实现的代码如下:

#define __KERNEL__

#include … //包含需要的头文件

#define … //定义相关变量

/*******************************************************************

 *get the RSSI value

 *******************************************************************/

int GetRSSIvalue(void)

 {

 int8_t rssivalue;

 const char *ifname;

 char aa[5] = {"ath0"};

 ifname = aa;

 u_char buf[24*1024];

 struct iwreq iwr;

 u_char *cp;

 int s, len;

 s = socket(AF_INET, SOCK_DGRAM, 0);

 if (s < 0)

 err(1, "socket(SOCK_DRAGM)");

 memset(&iwr, 0, sizeof(iwr));

 strncpy(iwr.ifr_name, ifname, sizeof(iwr.ifr_name));



 iwr.u.data.pointer = (void *) buf;

 iwr.u.data.length = sizeof(buf);

 if (ioctl(s, IEEE80211_IOCTL_STA_INFO, &iwr) < 0)

 errx(1, "unable to get station information");

 len = iwr.u.data.length;

 if (len < sizeof(struct ieee80211req_sta_info))

 return;



 cp = buf;

 do {

 struct ieee80211req_sta_info *si;

 u_char *vp;

 si = (struct ieee80211req_sta_info *) cp;

 vp = (u_int8_t *)(si+1);

 rssivalue = si->isi_rssi;

 cp += si->isi_len, len -= si->isi_len;

 } while (len >= sizeof(struct ieee80211req_sta_info));

 return(rssivalue);

}

/*******************************************************************

 *main function

 *******************************************************************/

int main(int argc,char **argv)

{

 int rssivalue;

 int senddata;

 int RSSIprecision;

 char inputstate[2];

 char databuf[2];

 const int maxrssi=66;

 const int minrssi=2;

 int fd;

 int size;

if((fd=open("/dev/AD5310",O_RDWR))==-1) //打开设备文件

{

 perror("device open fail\n");

 exit(1)

}

 RSSIprecision=1023/maxrssi;

 Delay(5);

 while(1)

 {

 size = read(fd,inputstate,1,0); //读取GPIO12状态,查看是否有Meter接入

 if(inputstate[0]==1) //no meter,high

 {

 printf("RSSI not enabled!\n");

 printf("Please mount RSSI meter!\n");

 Delay(1000);

 }

 else //有Meter接入

 {

 printf("mount RSSI meter OK!");

 rssivalue = GetRSSIvalue(); //从系统中获得RSSI的值

 printf("RSSI:%4d",rssivalue);

 if(rssivalue > maxrssi)

 { //over load,display max vcc

 senddata = 1023;

 }

 else if(rssivalue <= minrssi)

 {

 senddata = 2; // output is too low

 }

 else

 {

 senddata = RSSIprecision *rssivalue; //calculate the data needed to send

 }

 databuf[1]=senddata;

 databuf[0]= senddata >> 8;

 write(fd,databuf,2,0); //把数据发送给AD5310

 printf("databuf[1]=%d\n",databuf[1]);

 printf("databuf[0]=%d\n",databuf[0]);

 sleep(1); //延时

 }

 }

}

/**************************************************************/

/*end of RSSI_AD5310*/



另外,在编写真正的应用程序之前,为了保证驱动程序的正确性,一般会先写一些小的测试应用程序来测试驱动,在驱动正确的情况下才会编写测试我们需要的应用程序。

阅读(1785) | 评论(0) | 转发(0) |

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值