此代码为本人原创。该代码仍有不完善之处,有可能还要再次修改!仅供参考!
若有错误、疑问和意见请留言,非常感谢!
该驱动程序基于TQ2440开发板,内核2.6.30。
驱动程序比较简单,使用字符设备来实现。要注意的是在模拟DS18B20的总线时序时,处理器不能抢占当前线程从而造成时序错乱,因此使用了自旋锁来禁止处理器抢占。
代码比较简单,所以代码注释也比较少。如果有不明白的请参考DS18B20的datasheet以及<
NOTE:请使用驱动代码二,代码一不支持多进程访问。
//using cdev to create device
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "ds18b20"
#define SKIP_ROM 0xcc
#define CONVERT_T 0x44
#define READ_SCRATCHPAD 0xbe
#define DQ S3C2410_GPB5
MODULE_LICENSE("GPL");
static struct ds18b20_info
{
struct mutex ds18b20_mutex;
spinlock_t ds18b20_spinlock;
};
static int ds18b20_init_work(struct ds18b20_info *dsin)
{
char n;
unsigned long flag;
spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);
s3c2410_gpio_setpin(DQ, 1);
udelay(70);
s3c2410_gpio_setpin(DQ, 0);
udelay(500);
s3c2410_gpio_setpin(DQ, 1);
udelay(70);
n = s3c2410_gpio_getpin(DQ);
if(n == 1)
n = -1;
udelay(80);
spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);
return n;
}
static void ds18b20_write_byte(unsigned char data, struct ds18b20_info *dsin){
unsigned char i;
unsigned long flag;
spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);
for (i = 0; i < 8; i++){
s3c2410_gpio_setpin(DQ, 0);
udelay(10);
s3c2410_gpio_setpin(DQ, (data&0x01)? 1:0);
udelay(40);
s3c2410_gpio_setpin(DQ, 1);
data >>= 1;
udelay(1);
}
spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);
}
static unsigned char ds18b20_read_byte(struct ds18b20_info *dsin){
unsigned char i, value= 0;
unsigned long flag;
spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);
for(i = 0; i < 8; i++){
value >>= 1;
s3c2410_gpio_setpin(DQ, 0); //pull low
s3c2410_gpio_setpin(DQ, 1); //pull high
if (s3c2410_gpio_getpin(DQ)) //read
{
value |= 0x80;
}
udelay(40);
}
spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);
return value;
}
static int ds18b20_open(struct inode *inode, struct file *file)
{
struct ds18b20_info *dsin = kmalloc(sizeof(* dsin), GFP_KERNEL);
if(dsin == NULL){
printk("No memory for ds18b20\n");
return -ENOENT;
}
memset(dsin, 0, sizeof(*dsin));
file->private_data = dsin;
spin_lock_init(&dsin->ds18b20_spinlock);
mutex_init(&dsin->ds18b20_mutex);
printk("<1>open ds18b20 \n");
return 0;
}
static int ds18b20_close(struct inode *inode, struct file *file)
{
struct ds18b20_info *dsin = file->private_data;
kfree(dsin); //必需释放
return 0;
}
static int ds18b20_read (struct file *filp, char *buff, size_t __user count, loff_t *offp)
{
struct ds18b20_info *dsin = filp->private_data;
unsigned char temp1, temp2;
unsigned char buffer[2];
unsigned long err;
mutex_lock(&dsin->ds18b20_mutex);
if(ds18b20_init_work(dsin) < 0)
printk("ds18b20 unavailable\n");
ds18b20_write_byte(SKIP_ROM, dsin);
ds18b20_write_byte(CONVERT_T, dsin);
ssleep(1);
ds18b20_init_work(dsin);
ds18b20_write_byte(SKIP_ROM, dsin);
ds18b20_write_byte(READ_SCRATCHPAD, dsin);
temp1 = ds18b20_read_byte(dsin);
temp2 = ds18b20_read_byte(dsin);
mutex_unlock(&dsin->ds18b20_mutex);
buffer[0] = temp1;
buffer[1] = temp2;
err = copy_to_user(buff, (void *)buffer, count);
return err? -EFAULT:count ;
}
static struct file_operations ds18b20_fops =
{
.owner = THIS_MODULE,
.open = ds18b20_open,
.read = ds18b20_read,
.release = ds18b20_close,
};
struct cdev my_cdev;
dev_t dev_num;
static char __initdata banner[]="ds18b20 sensor forth edition, made by yanjun\n";
static struct class *led_class;
static int __init ds18b20_init(void)
{
int ret, err;
//get the device number in a dynamic way
if ((ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME)) < 0)
printk("alloc_chdrev_region error");
printk("dev_num = %d\n", MAJOR(dev_num));
//create and initialize a cdev in a dynamic way
cdev_init(&my_cdev, &ds18b20_fops);
my_cdev.owner = THIS_MODULE;
// my_cdev.ops = &ds18b20_fops;
err = cdev_add(&my_cdev, dev_num, 1);
if (err < 0)
printk("cdev error");
printk(banner);
//create a device node
led_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(led_class)){
printk("Err:failed in embedsky_leds class.\n");
return -1;
}
device_create(led_class, NULL, dev_num, NULL, DEVICE_NAME);
printk(DEVICE_NAME"initialized\n");
return 0;
}
static void __exit ds18b20_exit(void)
{
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
device_destroy(led_class, dev_num);
class_destroy(led_class); }
module_init(ds18b20_init);
module_exit(ds18b20_exit);
该代码基于代码一,进行了简单的修改,使用platform机制,修正了不能多进程访问的bug。
//using cdev to create device
#include
#include
#include
#include
#include
#include
#include
#include
//#include
//#include
//#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "ds18b20"
#define SKIP_ROM 0xcc
#define CONVERT_T 0x44
#define READ_SCRATCHPAD 0xbe
#define DQ S3C2410_GPB5
MODULE_LICENSE("GPL");
struct ds18b20_info
{
struct mutex ds18b20_mutex;
spinlock_t ds18b20_spinlock;
};
struct ds18b20_info *dsin;
static int ds18b20_init_work(struct ds18b20_info *dsin)
{
char n;
unsigned long flag;
spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);
s3c2410_gpio_setpin(DQ, 1);
udelay(70);
s3c2410_gpio_setpin(DQ, 0);
udelay(500);
s3c2410_gpio_setpin(DQ, 1);
udelay(70);
n = s3c2410_gpio_getpin(DQ);
if(n == 1)
n = -1;
udelay(80);
spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);
return n;
}
static void ds18b20_write_byte(unsigned char data, struct ds18b20_info *dsin){
unsigned char i;
unsigned long flag;
spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);
for (i = 0; i < 8; i++){
s3c2410_gpio_setpin(DQ, 0);
udelay(10);
s3c2410_gpio_setpin(DQ, (data&0x01)? 1:0);
udelay(40);
s3c2410_gpio_setpin(DQ, 1);
data >>= 1;
udelay(1);
}
spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);
}
static unsigned char ds18b20_read_byte(struct ds18b20_info *dsin){
unsigned char i, value= 0;
unsigned long flag;
spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);
for(i = 0; i < 8; i++){
value >>= 1;
s3c2410_gpio_setpin(DQ, 0); //pull low
s3c2410_gpio_setpin(DQ, 1); //pull high
if (s3c2410_gpio_getpin(DQ)) //read
{
value |= 0x80;
}
udelay(40);
}
spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);
return value;
}
static int ds18b20_open(struct inode *inode, struct file *file)
{
file->private_data = dsin;
printk("<1>open ds18b20 \n");
return 0;
}
static int ds18b20_close(struct inode *inode, struct file *file)
{
printk("Now kfree has been executed");
return 0;
}
static int ds18b20_read (struct file *filp, char *buff, size_t __user count, loff_t *offp)
{
struct ds18b20_info *dsin = filp->private_data;
unsigned char temp1, temp2;
unsigned char buffer[2];
unsigned long err;
mutex_lock(&dsin->ds18b20_mutex);
if(ds18b20_init_work(dsin) < 0)
printk("ds18b20 unavailable\n");
ds18b20_write_byte(SKIP_ROM, dsin);
ds18b20_write_byte(CONVERT_T, dsin);
ssleep(1);
ds18b20_init_work(dsin);
ds18b20_write_byte(SKIP_ROM, dsin);
ds18b20_write_byte(READ_SCRATCHPAD, dsin);
temp1 = ds18b20_read_byte(dsin);
temp2 = ds18b20_read_byte(dsin);
mutex_unlock(&dsin->ds18b20_mutex);
buffer[0] = temp1;
buffer[1] = temp2;
msleep(20);
err = copy_to_user(buff, (void *)buffer, count);
return err? -EFAULT:count ;
}
static struct file_operations ds18b20_fops =
{
.owner = THIS_MODULE,
.open = ds18b20_open,
.read = ds18b20_read,
.release = ds18b20_close,
};
struct cdev my_cdev;
dev_t dev_num;
static char __initdata banner[] = "Ds18b20 sensor: fifth edition using platform, made by yanjun\n";
static struct class *led_class;
static int s3c2410_ds18b20_probe(struct platform_device *pdev)
{
int ret, err;
//get the device number in a dynamic way
if ((ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME)) < 0)
printk("alloc_chdrev_region error");
printk("dev_num = %d\n", MAJOR(dev_num));
//create and initialize a cdev in a dynamic way
cdev_init(&my_cdev, &ds18b20_fops);
my_cdev.owner = THIS_MODULE;
err = cdev_add(&my_cdev, dev_num, 1);
if (err < 0)
printk("cdev error");
printk(banner);
//create a device node
led_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(led_class)){
printk("Err:failed in embedsky_leds class.\n");
return -1;
}
device_create(led_class, NULL, dev_num, NULL, DEVICE_NAME);
dsin = kmalloc(sizeof(* dsin), GFP_KERNEL);
if(dsin == NULL){
printk("No memory for ds18b20\n");
return -ENOENT;
}
memset(dsin, 0, sizeof(*dsin));
spin_lock_init(&dsin->ds18b20_spinlock);
mutex_init(&dsin->ds18b20_mutex);
printk(DEVICE_NAME"initialized\n");
return 0;
}
static void __exit s3c2410_ds18b20_remove(void)
{
kfree(dsin);
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
device_destroy(led_class, dev_num);
class_destroy(led_class);
}
#ifdef CONFIG_PM
static int s3c2410_ds18b20_suspend(struct platform_device *pdev, pm_message_t msg)
{
/*nothing to do*/
return 0;
}
static int s3c2410_ds18b20_resume(struct platform_device *pdev, pm_message_t msg)
{
/*nothing to do*/
return 0;
}
#else
#define s3c2410_ds18b20_suspend NULL
#define s3c2410_ds18b20_resume NULL
#endif
static struct platform_driver s3c2410_ds18b20_driver = {
.probe = s3c2410_ds18b20_probe,
.remove = s3c2410_ds18b20_remove,
.suspend = s3c2410_ds18b20_suspend,
.resume = s3c2410_ds18b20_resume,
.driver = {
.name = "s3c2410-ds18b20",
.owner = THIS_MODULE,
},
};
static int __init s3c2410_ds18b20_init(void)
{
return platform_driver_register(&s3c2410_ds18b20_driver);
}
static void __exit s3c2410_ds18b20_exit(void)
{
platform_driver_unregister(&s3c2410_ds18b20_driver);
}
module_init(s3c2410_ds18b20_init);
module_exit(s3c2410_ds18b20_exit);
在ds18b20_read函数中,添加了20ms的休眠,该句用于实现多进程的访问。不添加的话,在我的平台上,只有一个进程读取到温度值。
第一个:arch/arm/plat-s3c24xx/devs.c
添加内容:
/*DS18b20 temperature sensor, added by yj423*/
static u64 s3c_device_ds18b20_dmamask = 0xffffffffUL;
struct platform_device s3c_device_ds18b20 = {
.name = "s3c2410-ds18b20",
.id = -1,
.num_resources = 0,
.resource = NULL,
.dev = {
.dma_mask = &s3c_device_ds18b20_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
EXPORT_SYMBOL(s3c_device_ds18b20);
第二个: arch/arm/plat-s3c/include/plat/devs.h
添加内容:
/*added by yj423*/
extern struct platform_device s3c_device_ds18b20;
第三个:arch/arm/mach-s3c2440/mach-smdk2440.c
在smdk2440_devices中添加&s3c_device_ds18b20,
编译,加载模块后,我们去sysfs文件系统去验证一下:
[root@yj423 s3c2410-ds18b20]#pwd
/sys/bus/platform/devices/s3c2410-ds18b20
[root@yj423 s3c2410-ds18b20]#ll
lrwxrwxrwx 1 root root 0 Jan 1 00:33 bus -> ../../../bus/platform
lrwxrwxrwx 1 root root 0 Jan 1 00:33 driver -> ../../../bus/platform/drivers/s3c2410-ds18b20
-r--r--r-- 1 root root 4096 Jan 1 00:33 modalias
drwxr-xr-x 2 root root 0 Jan 1 00:33 power
lrwxrwxrwx 1 root root 0 Jan 1 00:33 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jan 1 00:33 uevent
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
int i, temper_fd;
unsigned char value[2];
unsigned char temp;
float temperature;
i = 0;
if((temper_fd = open("/dev/ds18b20", 0)) < 0){
perror("open device ds18b20 error");
printf("exit\n");
exit(1);
}
// printf("before syscall read\n");
for(; ;){
int ret = read(temper_fd, value, sizeof(value));
if (ret != 2){
printf("read wrong\n");
exit(0);
}
temp = value[0] & 0x0f;
value[1] = value[1] << 4 | value[0] >> 4;
temperature = (float)value[1] + temp * 6.0 / 100.0;
printf("temperature = %.2f ", temperature);
fflush(stdout);
i ++;
if (i == 4){
printf("\n");
i = 0;
}
}
close(temper_fd);
return 0;
}
运行结果:
[root@yj423 modules]#insmod ds18b20.ko
dev_num = 252
ds18b20 sensor forth edition, made by yanjun
ds18b20initialized
[root@yj423 modules]#./sensor2
open ds18b20
temperature = 32.48 temperature = 32.48 temperature = 32.48 temperature = 32.54
temperature = 32.54 temperature = 32.54 temperature = 32.54 temperature = 32.54
temperature = 32.60 temperature = 32.54 temperature = 32.60 temperature = 32.60
temperature = 32.72 temperature = 32.84 temperature = 33.00 temperature = 33.18
temperature = 33.36 temperature = 33.48 temperature = 33.60 temperature = 33.66
temperature = 33.72 temperature = 33.78 temperature = 33.72 ^CNow kfree has been executed