Linux驱动开发:基于全志A40i的Linux单总线驱动的开发(AM2301)

基于全志A40i的Linux单总线驱动的开发(AM2301温湿度传感器)

本文主要是介绍在Linux系统上驱动AM2301,实现读取到当前环境的温湿度数据。通过这个来记录和分享一下在Linux系统下单总线设备的驱动该怎么写(Linux驱动初学者)。

一、驱动代码实现

最终的实现是采用一个gpio口模拟的单总线,来与AM2301通讯,通过注释来解释说明(本文侧重点在驱动的实现上,关于AM2301相关的介绍和单总线的知识,网上有很多,我这就不罗嗦了,下面上代码)。

/*************************************************************************
 > File Name: AM2301.c
 > Author:jonker
 > address: 
 > Created Time:  10时42分19秒
 ************************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rwsem.h>
#include <linux/timer.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/sysfs.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/sys_config.h>
#include "../base/base.h"
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <asm/io.h>

#define DEV_NAME "AM2301"
#define AM2301_GPIO GPIOG(4) 

volatile unsigned long * GPIO_DAT;

static struct cdev demo_cdev;
struct class *class = NULL;
struct device *device = NULL;
unsigned int major = 0;
int AM2301_GPIO = AM2301_GPIO;

struct mutex res_mutex;  // 采用信号量来防止数据竞争的出现

void setL(void) {
        printk("into setL !\n");
        gpio_set_value(AM2301_GPIO, 0);
}

void setH(void) {
        printk("into setH !\n");
        gpio_set_value(AM2301_GPIO, 1);
}

void setOutput(void) {
        printk("into setOutput !\n");
        gpio_direction_output(AM2301_GPIO, 1);  // 设为输出模式
}

void setInput(void) {
        printk("into setInput !\n");
        gpio_direction_input(AM2301_GPIO);  // 设为输入模式
}

int getOutput(void) {
    if(readl(GPIO_DAT)&0x10) {
            return 1;
        }
        return 0;
}
int ow_start(void) {
        char timeOut = 0;
        printk("into ow_start !\n");
        setOutput();
        setH();
        udelay(100);
        setL();
        udelay(1000);  // 拉低至少800us
        setH();

        udelay(20);

        setInput();
//      printk("setInput######\n");
        if(__gpio_get_value(AM2301_GPIO) == 0) {
                timeOut = 0;
                while(!__gpio_get_value(AM2301_GPIO)) {  // 最多80us
                        timeOut++;
                        udelay(1);
                        if(timeOut > 80)
                                goto END1;
                }
                timeOut = 0;
                while(__gpio_get_value(AM2301_GPIO)) {  // 最多80us
                        timeOut++;
                        udelay(1);
                        if(timeOut > 80)
                                goto END1;
                }
                return 0;
        }
        printk("start Error!\n");
        return -1;
END1:
        printk("start TimeOut!\n");
        return -1;
}
void ow_read(unsigned char *sbuf) {
        unsigned char i, j, data;
        unsigned int timeOut = 0;
        for(j = 0; j < 5; j++) {
            data = 0;
                for(i = 0; i < 8; i++) {
                        data <<= 1;
                        timeOut = 1;
                        while((getOutput()) && timeOut < 65536) {
                                timeOut++;
                        }
                        if(timeOut >= 65535) {
                                sbuf[j] = data;
                                goto END;
                        }
                        timeOut = 1;
                        while((!getOutput()) && timeOut < 65535) {//  50us Low 
                                timeOut++;
                        }
                        if(timeOut >= 65535) {
                                sbuf[j] = data;
                                goto END;
                        }
            timeOut = 1;

                        udelay(30);                                         // 延时35us 判断low or hig
                        if(getOutput()) {
                                data |= 0x01;
                                
                        } else {
                                data &= 0xfe;
                        }
                        timeOut = 1;
                }
                sbuf[j] = data;
        }
        return;
END:
                printk("ow_read TimeOut!\n");
                return;
}
static int AM2301_open(struct inode *inodeP, struct file *fileP)
{
        printk("device open success!\n");
        return 0;
}

static ssize_t AM2301_read(struct file *pFile, char __user *buf, size_t count, loff_t *off) {
	unsigned char sum = 0xff;
	//unsigned char sbuf = 0, check = 0;
	unsigned char tdata[5];
	mutex_lock(&res_mutex); // 上锁
	if(ow_start() == 0) {
	        ow_read(tdata);
	        sum = (tdata[0] + tdata[1] + tdata[2] + tdata[3]);
	//              printk("read: %d,%d,%d,%d,%d,%d\n",(int)tdata[0], (int)tdata[1], (int)tdata[2], (int)tdata[3], (int)tdata[4], (int)sum);
	
	        mutex_unlock(&res_mutex);
	
	        if(tdata[4] != sum)
	                return -1;
	        if(copy_to_user(buf, tdata, 4))
	                return -1;
	        return 4;
	} else {
	    printk("AM2301_read ow_start faile!\n");
	    mutex_unlock(&res_mutex);
	}
	
	return -1;
}

static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
    int err;
    int devno;
    devno = MKDEV(major, minor);
    cdev_init(dev, fops);
    dev->owner = THIS_MODULE;
    dev->ops = fops;
    err = cdev_add(dev, devno, 1);
    if (err) {
            printk(KERN_NOTICE" Error %d adding dev %d", err, minor);
    }
}

static struct file_operations AW2301_fpos={
	.owner = THIS_MODULE,
	.open = AM2301_open,
	//      .close = AM2301_close,
	.read = AM2301_read,
};

static int __init AW2301_init(void) {
	int re;
	dev_t dev = MKDEV(major, 0);
	printk("into AW2301_init\n");
	mutex_init(&res_mutex);
	GPIO_DAT = ioremap(0x01C20800+0x00E8, 8);  // 将GPIOG4口的数据寄存器映射过来
	//为字符设备分配设备号
	if (major) {
	        re = register_chrdev_region(dev, 1, DEV_NAME);
	} else {
	        re = alloc_chrdev_region(&dev, 0, 1, DEV_NAME); // 未指定主设备号,系统自动分配
	}
	
	if (re < 0) {
       printk(KERN_WARNING" Demo dev-->unable to get major %d\n", major);
       return re;
	}
	major = MAJOR(dev);
	setup_cdev(&demo_cdev, 0, &AW2301_fpos); // 初始化并将注册字符设备
	printk("The major of the demo device is %d\n", major);
	
	class = class_create(THIS_MODULE,DEV_NAME); /*在sys下创建类目录/sys/class/AM2301*/
	device_create(class, NULL, MKDEV(major,0), NULL, DEV_NAME); // 自动创建设备节点 
	if (gpio_request(AM2301_GPIO, NULL)) {
	        pr_err("AM2301_GPIO:%d gpio_request fail\n", AM2301_GPIO);
	}
	return 0;
}

static void __exit Aw2301_exit(void) {
	printk("into Aw2301_exit\n");
	gpio_free(AM2301_GPIO);
	device_destroy(class, MKDEV(major,0));// 销毁设备
	class_unregister(class); // 注销类
	cdev_del(&demo_cdev);  // 注销设备
	unregister_chrdev_region(MKDEV(major, 0), 1); // 释放设备号
	printk("Demo device uninstalled\n");
}

module_init(AW2301_init);
module_exit(Aw2301_exit);
MODULE_AUTHOR("jonker 1432603260@qq.com");
MODULE_DESCRIPTION("one wires AW2301");
MODULE_LICENSE("GPL");

二、测试应用层程序

/*************************************************************************
 > File Name: test-AM2301.c
 > Author:jonker
 > address:  
 > Created Time: 17时07分10秒
 ************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main()
{
    int fd = -1;
        int value = 0;
        float tem= 0.0, htm = 0.0;
        unsigned char dst[4];

        fd = open("/dev/AM2301", O_RDWR);
        if(fd < 0) {
                printf("open error!\n");
                return -1;
        }
        read(fd, &dst, 4); // 第一次读取到的数据是错误的,需要连连续读两次
        sleep(1);
        while(1) {
                read(fd, &dst, 4);
                printf("AM2301 data is %d,%d,%d,%d\n",dst[0],dst[1],dst[2],dst[3]);
        value = (int)(dst[2]<< 8 | dst[3]);
                tem = value/10.0;
        value = (int)(dst[0]<< 8 | dst[1]);
                htm = value/10.0;
                printf("tem: %f; hum: %f\n",tem,htm);
                sleep(3);
        }
        close(fd);
        return 0;
}

三、后记

本文知识点:
1、学习Linux字符驱动框架的开发;
2、在Linux驱动中如何设置普通gpio口工作模式和设置以及读取gpio的值;
3、通过gpio口模拟单总线。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值