本程序是在大三实习阶段的时候写的一个驱动程序,当时对于驱动程序的理解不是很彻底,只是在别人的框架下自己去填写代码,先把ds18b20的时序图读懂,照着时序图写代码就可以了,但是要注意的就是时序图中的延时问题,在满足要求的情况下,尽量延时长一点!!!写驱动程序还有一点很重要的就是会自己测试程序,要在能尽可能的测试到驱动程序最容易出错的地方。
1.以下是驱动程序代码:
注:本程序在飞凌OK6410开发板上测试通过,其它的开发板也一样,只要做很少的改动应该就可以运行,不过,本程序只实现了一块ds18b20的情况,比较简单。多块的情况也可以通过芯片手册做一定的改动,不过,我没试过。
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-e.h>
#include <mach/gpio-bank-m.h>
#define DEVICE_NAME "ds18b20"
unsigned int tmp;
int output_mode(void)
{
tmp=readl(S3C64XX_GPECON);
tmp&=(~0x0F);
tmp|=0x01;
writel(tmp,S3C64XX_GPECON);
return 0;
}
int input_mode(void)
{
tmp=readl(S3C64XX_GPECON);
tmp&=(~0x0F);
tmp|=0x00;
writel(tmp,S3C64XX_GPECON);
return 0;
}
int output_bit(unsigned int bit)
{
tmp=readl(S3C64XX_GPEDAT);
tmp&=(~0x01);
tmp|=bit;
writel(tmp,S3C64XX_GPEDAT);
return 0;
}
unsigned int input_bit(void)
{
tmp=readl(S3C64XX_GPEDAT);
tmp&=0x01;
// printk("In Input_bit tmp is %d\n",tmp);
return tmp;
}
int init_DS18B20(void)
{
unsigned int tp=0;
int waittime=10;
output_mode();
output_bit(1);
output_bit(0);
udelay(480);
input_mode();
udelay(15);
tp=input_bit();
while(1)
{
if(!tp||waittime>500)
break;
udelay(5);
waittime+=5;
tp=input_bit();
// printk("tp is %d\n",tp);
}
if(!tp)
{
udelay(480);
output_mode();
output_bit(1);
printk("Init Success\n");
return 0;
}
else
{
printk("Failed\n");
return -1;
}
}
int write_to_DS18B20(unsigned char byte)
{
unsigned char i,k;
for(i=0;i<=7;i++)
{
k=(byte>>i)&(0x01);
if(k==1)
{
output_mode();
output_bit(1);
output_bit(0);
udelay(1);
input_mode();
udelay(45);
}
else
{
output_mode();
output_bit(1);
output_bit(0);
udelay(60);
input_mode();
}
}
return 0;
}
unsigned char read_from_DS18B20(void)
{
unsigned int i,j;
unsigned char byte=0x00;
for(i=0;i<=7;i++)
{
output_mode();
output_bit(1);
output_bit(0);
udelay(1);
input_mode();
udelay(10);
j=input_bit();
if(j==1)
byte|=1<<i;
udelay(45);
}
return byte;
}
int config_DS18B20(void)
{
init_DS18B20(); //initialization *********************
write_to_DS18B20(0xCC); //skip rom
write_to_DS18B20(0x4E); //write scratchpad
write_to_DS18B20(0x55); //set TH 85
write_to_DS18B20(0x00); //set TL 0
write_to_DS18B20(0x1F); //Resolution: set 9 bit, (0.5)
return 0;
}
unsigned short get_temperature(void)
{
unsigned char LSB;
unsigned char MSB;
unsigned short t;
init_DS18B20();
// udelay(120);
write_to_DS18B20(0xcc);
write_to_DS18B20(0x44);
// udelay(5);
init_DS18B20();
// udelay(200);
write_to_DS18B20(0xcc);
write_to_DS18B20(0xbe);
LSB=read_from_DS18B20();
MSB=read_from_DS18B20();
t=MSB*256+LSB;
// printk("MSB is %d LSB is %d\n",MSB,LSB);
return t;
}
static long s3c64xx_temperature_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
unsigned short temp = 0;
temp = get_temperature();
// printk("The Temperature is %d\n",temp);
return temp;
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl= s3c64xx_temperature_ioctl,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
config_DS18B20();
ret = misc_register(&misc);//注意使用混杂设备注册可以减少很多不必要的初始化
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("chenjun");
2.驱动程序的Makefile如下:
obj:
make -C /shell/linux-2.6.36.2-v1.05/ SUBDIRS=/shell/ds18b20/ modules
注: /shell/linux-2.6.36.2-v1.05/ 是我用的linux的内核目录
驱动程序所在目录:SUBDIRS=/shell/ds18b20/
以模块的方式加载:modules
3.以下是测试程序代码:
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/mman.h>
#define EPS 0.5
#define np 0.0625
void warn(int fd);
int main()
{
int fd,old,new,led;
double result=0;
if ((fd=open("/dev/ds18b20",O_RDWR)) < 0)
{
printf("Open Device DS18B20 failed.\r\n");
exit(1);
}
if ((led=open("/dev/leds",O_RDONLY)) < 0)
{
printf("Open Device leds failed.\r\n");
exit(1);
}
old=new=0;
while(1)
{
new=ioctl(fd,0,0);
if(fabs((abs(old-new)*np))>EPS)
{
warn(led);
}
result=(double)new*np;
printf("%.2lf \n", result);
sleep(1);
old=new;
}
close(fd);
return 0;
}
void warn(int fd)
{
int i,t;
for(i=0;i<40;i++)
{
t=i&1;
ioctl(fd, t, 0);
ioctl(fd, t, 1);
ioctl(fd, t, 2);
ioctl(fd, t, 3);
usleep(5000);
}
}
注:
以上的测试程序在一定程度上有点Bug,由于实习完后,没有开发板了,所以没有再做修改。也没有测试了。