【源码】基于I.MX6ull驱动移植ds18b20的实验详解



前言

提示:基于I.MX6ull驱动移植ds18b20的实验:

实验平台:正点原子alpha开发板V2.2
传感器:ds18b20模块


一、硬件连接

ds18b20的VCC:连接开发板3.3V;
ds18b20的GND:连接开发板GND;
ds18b20的信号线:连接alpha开发板的CSI_VSYNC引脚,查手册可知。
在这里插入图片描述

在这里插入图片描述

请添加图片描述

二、代码移植

1.驱动代码

1.创建一个ds18b20.c文件,在文件中输入以下代码:

代码如下(示例):

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : ds18b20.c
作者       : wj learn tangmingfei2013@126.com
版本       : V1.0
描述       : ds18b20 驱动程序, GPIO4_PIN19-----ds18b20 port
其他       : 无
日志       : 初版V1.0 2024/4/20
使用方法:
    typedef struct{
        unsigned short  temperatureVal; //温度数据, 真实值val = temperatureVal *0.0625
        int sign;                       //符号位,1: 负数, 0: 正数
    }Ds18b20Struc;
    Ds18b20Struc ds_struc;
    
    void convert_temp()
    {
        read(fd, &ds_struc, sizeof(Ds18b20Struc));
        printf("ds18b20 Value: %.02f, sign: %d \n", ds_struc.temperatureVal*0.0625, 
                                                    ds_struc.sign);
    }
***************************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
 
#define MODE_NAME      "ds18b20"     // dev/ds18b20
#define DEVICE_CNT     1
 
#define CCM_CCGR3_BASE                          (0x20C4074)   // GPIO端口时钟配置
#define IOMUXC_SW_MUX_CTL_PAD_CSI_VSYNC_BASE    (0x20E01DC)   // IO_PIN 功能属性配置
#define IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC_BASE    (0x20E0468)   // IO_PIN 电平属性配置
 
#define GPIO4_GDIR_BASE                         (0x20A8004)   // GPIO 方向属性配置
#define GPIO4_DR_BASE                           (0x20A8000)   // GPIO 数据寄存器
 
#define PIN_SEC                                 19            // GPIO - 19 
 
 
static void __iomem *CCM_CCGR;
static void __iomem *IOMUXC_SW_MUX_CTL ;
static void __iomem *IOMUXC_SW_PAD_CTL ;
 
static void __iomem *GPIO_GDIR;
static void __iomem *GPIO_DR;
 
typedef struct{
    unsigned short   temperatureVal;    // 温度数据, 真实值val = temperatureVal *0.0625
    int sign;                           // 符号位,1: 负数, 0: 正数
}Ds18b20Struc;
 
/* 1.  struds18b20设备结构体 */
struct struds18b20_dev
{
    dev_t devid;            /* 设备号      */
    struct cdev cdev;       /* cdev     */
    struct class *class;    /* 类        */
    struct device *device;  /* 设备 	    */
    int major;              /* 主设备号     */
    int minor;              /* 次设备号     */
    struct device_node *nd; /* 设备节点 */
};
 
 
static struct struds18b20_dev struds18b20; /* structure ds18b20 设备 */

static void gpio_init( void )
{
    unsigned int temp;
 
    /*
        gpio port: GPIO4-PIN-19
    */
 
    // remap the register address to MCU
    CCM_CCGR = ioremap(CCM_CCGR3_BASE, 4);  
    IOMUXC_SW_MUX_CTL = ioremap(IOMUXC_SW_MUX_CTL_PAD_CSI_VSYNC_BASE, 4);
    IOMUXC_SW_PAD_CTL = ioremap(IOMUXC_SW_PAD_CTL_PAD_CSI_VSYNC_BASE, 4); 
 
    GPIO_GDIR = ioremap(GPIO4_GDIR_BASE, 4);
    GPIO_DR = ioremap(GPIO4_DR_BASE, 4);
 
    /*
    step-1:
        使能GPIO-4 CLOCK
        register CCM_CCGR3  
        Address: 20C_4000h base + 74h offset = 20C_4074h
        CG6: bit[13:12] = 0b11
    */
    temp = readl(CCM_CCGR);
    temp &= ~(3 << 12);             /* 清除以前的设置 */
    temp |= (3<<12);                /* 设置新值 */
    writel(temp, CCM_CCGR);
 
    /*
     step-2:
       设置GPIO4_PIN20为IO
       register IOMUXC_SW_MUX_CTL_PAD_CSI_HSYNC
       Address: 20E_0000h base + 1E0h offset = 20E_01E0h
       MUX_MODE: bit[3:0] = 0b0101
       */
    temp = readl(IOMUXC_SW_MUX_CTL); 
    temp &=~(0xf);
    temp |= 0x05;
    writel(temp, IOMUXC_SW_MUX_CTL);
 
    /*
    step-3:
        设置GPIO_GDIR,配置该IO为 in/out
        Address: Base address + 4h offset
        0 INPUT — GPIO is configured as input.
        1 OUTPUT — GPIO is configured as output.
    */
    temp = readl(GPIO_GDIR); 
    temp |= (1<<PIN_SEC);     //设置IO为输出
    writel(temp, GPIO_GDIR);
 
}
 
static void gpio_uninit( void )
{
    iounmap(CCM_CCGR);
    iounmap(IOMUXC_SW_MUX_CTL);
    iounmap(IOMUXC_SW_PAD_CTL);
    iounmap(GPIO_GDIR);
    iounmap(GPIO_DR);
}
 
static void gpio_set_input(void)
{
    unsigned int temp;
 
    /*
    step-3:
        设置GPIO_GDIR,配置该IO为 in/out
        Address: Base address + 4h offset
        0 INPUT — GPIO is configured as input.
        1 OUTPUT — GPIO is configured as output.
    */
    temp = readl(GPIO_GDIR); 
    temp &= ~(1<<PIN_SEC);       //设置IO为输入
    writel(temp, GPIO_GDIR);    
}
 
static void gpio_set_output(void)
{
    unsigned int temp;
 
    /*
        step-3:
            设置GPIO_GDIR,配置该IO为 in/out
            Address: Base address + 4h offset
            0 INPUT — GPIO is configured as input.
            1 OUTPUT — GPIO is configured as output.
    */
    temp = readl(GPIO_GDIR); 
    temp |= (1<<PIN_SEC);        //设置IO为输入
    writel(temp, GPIO_GDIR);    
}
 
 
static void set_pin_data( int val )
{
    unsigned int temp;
 
    temp = readl(GPIO_DR);
    if( val ){
        temp |= (1<<PIN_SEC);
        writel(temp, GPIO_DR);
    }
    else{
        temp &= ~(1<<PIN_SEC);
        writel(temp, GPIO_DR);
    }
}
 
static bool get_pin_data(void)
{
    unsigned int temp;
    
    temp = readl(GPIO_DR);
    
    return (temp & (1<<PIN_SEC))> 0 ? true:false;
}

static void tempudelay(int usecs) 
{
    int pre,last;
 
    pre = ktime_get_boot_ns();
    
    while(1){
        last = ktime_get_boot_ns();
        if( (last - pre) >= (usecs*1000))
            break;
    }
}
 
static int ds18b20_wait_for_ack(void)
{
    int timeout_count = 500;
 
    gpio_set_input();
 
    /* 如果是高电平,等待 */
    while (get_pin_data() && --timeout_count)
    {
        udelay(1);
    }
 
    if (!timeout_count)
    return -1;
 
    /* 现在是低电平 */
    timeout_count = 500;
    while (!get_pin_data() && --timeout_count)
    {
        udelay(1);
    }
 
    if (!timeout_count)
        return -1;
 
    return 0;
}
 
 
static int ds18b20_check( void )
{
    gpio_set_output();
    set_pin_data(0);
 
    tempudelay(480);
 
    if (ds18b20_wait_for_ack())
        return -1;
    else
        return 0;
 
}
 
static void ds18b20_write_byte( unsigned char byte)
{
    unsigned char k;
 
    // write data bit
    gpio_set_output(); 
 
    for ( k = 0; k < 8; k++ )
    {
        if (byte & (1<<k))
        {
            set_pin_data(0);
            tempudelay(2); 
 
            set_pin_data(1);
            tempudelay(65); 
        }
        else
        {
            set_pin_data(0);
            tempudelay(65); 
 
            set_pin_data(1);
            tempudelay(2); 
        }
    }
}
 
 
static unsigned char ds18b20_read_byte( void )
{
    unsigned char byteVal = 0;
    unsigned char i;
 
    for ( i = 0; i < 8; i++ )
    {
        gpio_set_output();
        set_pin_data(0);
        tempudelay(2);
 
        gpio_set_input();
        tempudelay(7);
 
        if( get_pin_data() )
            byteVal |= (1<<i);
 
        tempudelay(60); 
    }
 
    return  byteVal;
}
 
static bool ds18b20_process( Ds18b20Struc *pdsStruc)
{
    unsigned long tempval;
    unsigned long flags;
    unsigned char temp1, temp2;
 
    local_irq_save(flags);
 
    if (ds18b20_check() == -1)
    {
        gpio_set_output();
        local_irq_restore(flags); 
        return false;
    }
    tempudelay(580);
 
    ds18b20_write_byte(0xcc);
    ds18b20_write_byte(0x44);
 
    gpio_set_output();
    set_pin_data(1);
 
    local_irq_restore(flags); 
 
    set_current_state(TASK_INTERRUPTIBLE);
    schedule_timeout(HZ); 
 
    local_irq_save(flags);
    if (ds18b20_check() == -1)
    {
        gpio_set_output();
        local_irq_restore(flags); 
        return false;
    }
    tempudelay(580);
 
    ds18b20_write_byte(0xcc);
    ds18b20_write_byte(0xbe);
 
    temp1 = ds18b20_read_byte();
    temp2 = ds18b20_read_byte();
 
    pdsStruc->sign = 0;
    if (temp2 > 0x7f)           //最高位为1时温度是负
    {
        temp1    = ~temp1;      //补码转换,取反加一
        temp2    = ~temp2+1; 
        pdsStruc->sign = 1;
    }
 
    //read tempeture value 
    tempval =  ((temp2 << 8) | temp1);
    printk(" [%s line %d ] read value: 0x%04x \r\n",
           __FUNCTION__, __LINE__, (u16)tempval);
 
    pdsStruc->temperatureVal = tempval;
 
    local_irq_restore(flags);
 
    return true;
}


static ssize_t ds18b20_read_val (struct file *file, char __user *buf, 
                                          size_t size, loff_t *offset)
{
    Ds18b20Struc dsStruc;
    bool result;
 
    result = ds18b20_process( &dsStruc );
    if( result ){
        result = copy_to_user(buf, &dsStruc, sizeof(Ds18b20Struc));
    }
 
    return sizeof(Ds18b20Struc);
}
 
 
static int ds18b20_drv_open(struct inode *node, struct file *file)
{
    //init device hardware 
    printk(" [%s line %d ] open the devices! \r\n",__FUNCTION__, __LINE__);
 
    return 0;
}
 
static int ds18b20_drv_close(struct inode *node, struct file *file)
{
    printk(" %s line %d \r\n",  __FUNCTION__, __LINE__);
 
    return 0;
}
 
/* 2. 定义自己的 file_operations 结构体 */
static struct file_operations ds18b20drv_drv = {
    .owner = THIS_MODULE,
    .open  = ds18b20_drv_open,
    .read = ds18b20_read_val,
    .release = ds18b20_drv_close,
};
 
/* 4. 把 file_operations 结构体告诉内核:注册驱动程序 */
/* 5. 安装驱动程序时,就会去调用这个入口函数 */
static int __init ds18b20_drv_init(void)
{
    printk("[%s line %d ] initial ds18b20 devices! \r\n",__FUNCTION__, __LINE__);
 
    /* 初始化硬件资源 */
    gpio_init();
 
    /* 注册字符设备驱动 */
 
    /* 1、创建设备号 */
    if (struds18b20.major)
    {
        /*  定义了设备号 */
        struds18b20.devid = MKDEV(struds18b20.major, 0);
        register_chrdev_region(struds18b20.devid, DEVICE_CNT, MODE_NAME);
    }
    else
    {
        /* 没有定义设备号,自动申请设备号 */
        alloc_chrdev_region(&struds18b20.devid, 0, DEVICE_CNT, MODE_NAME);
 
        struds18b20.major = MAJOR(struds18b20.devid); /* 获取分配号的主设备号 */
        struds18b20.minor = MINOR(struds18b20.devid); /* 获取分配号的次设备号 */
    }
 
    printk("struds18b20 major=%d,minor=%d \r\n", struds18b20.major, struds18b20.minor);
 
    /* 2、初始化cdev */
    struds18b20.cdev.owner = THIS_MODULE;
    cdev_init(&struds18b20.cdev, &ds18b20drv_drv);
 
    /* 3、添加一个cdev */
    cdev_add(&struds18b20.cdev, struds18b20.devid, DEVICE_CNT);
 
    /* 4、创建类 */
    struds18b20.class = class_create(THIS_MODULE, MODE_NAME);
    if (IS_ERR(struds18b20.class))
    {
        return PTR_ERR(struds18b20.class);
    }
 
    /* 5、创建设备 */
    struds18b20.device = device_create(struds18b20.class, NULL, 
                                       struds18b20.devid, NULL,
                                       MODE_NAME);
    if (IS_ERR(struds18b20.device))
    {
        return PTR_ERR(struds18b20.device);
    }
 
    return 0;
}
 
/* 6. 有入口函数就有出口函数:卸载驱动程序时就会去调用这个出口函数 */
static void __exit ds18b20_drv_exit(void)
{
    printk("[%s line %d ] exit ds18b20 drvices driver!\r\n",__FUNCTION__, __LINE__);
 
    gpio_uninit();
 
    /* 注销字符设备驱动 */
    cdev_del(&struds18b20.cdev);                             /*  删除cdev */
    unregister_chrdev_region(struds18b20.devid, DEVICE_CNT); /* 注销设备号 */
 
    device_destroy(struds18b20.class, struds18b20.devid);
    class_destroy(struds18b20.class);
}
 
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
 
module_init(ds18b20_drv_init);
module_exit(ds18b20_drv_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wj");

2.创建一个ds18b20App.c文件,在文件中输入以下代码:

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名      : ds18b20App.c
作者        : wj learn tangmingfei2013@126.com
版本        : V1.0
描述        : ds18b20 driver 测试程序,用于测试 drv_03_ds18b20
日志        : 初版V1.0 2024/4/20 
使用方法    : ./test_03_ds18b20
***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
 
#define devices   "/dev/ds18b20"
 
typedef struct{
    unsigned short  temperatureVal;     //温度数据, 真实值val = temperatureVal *0.0625
    int sign;                           //符号位,1: 负数, 0: 正数
}Ds18b20Struc;
 
 
void convert_temp( Ds18b20Struc *pStru )
{
    printf("ds18b20 Value: %.02f, sign: %d \n", pStru->temperatureVal*0.0625, 
                                                pStru->sign);
}
 
 
int main(void)
{
    int fd;
    int count_run = 10000;
    int len; 
    Ds18b20Struc ds_struc;
 
    fd = open(devices, 0);
    if (fd == -1){
        printf("can not open file: %s \n", devices);
        return -1;
    }
 
    while( count_run > 0)
    {
        len = read(fd, &ds_struc, sizeof(Ds18b20Struc));
        count_run--;
        if (len > 0) {
           convert_temp(&ds_struc);
        } 
        else {
           perror("read ds18b20 device fail!\n");
        }
        sleep(1);
    }
 
    close(fd);
 
    return 0;
}

3.创建一个Makefile文件,在文件中输入以下代码:
其中KERNELDIR 路径改为自己的交叉编译器

KERNELDIR := /home/wj/linux/alientek-linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)

obj-m := ds18b20.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

2.编译程序

1.在文件夹下输入make,之后输出ds28b20.ko内核模块
在这里插入图片描述
2.交叉编译器编译可执行文件

//生成可执行文件
arm-linux-gnueabihf-gcc -o ds18b20App ds18b20App.c
//查看文件类型
file ds18b20App 

在这里插入图片描述

三、移植到开发板

本方法使用nfs挂载的方法加载根文件系统,将上述生成的ds28b20.ko 文件和ds18b20App文件拷贝到自己为根文件夹下面;

加载内核模块

//使用insmod方法有效,测试使用modprobe无效
insmod ds18b20.ko

在这里插入图片描述

//查看设备是否存在
ls /dev/sd*

在这里插入图片描述

//执行可执行程序
./ds18b20

在这里插入图片描述
在这里插入图片描述

参考连接

https://blog.csdn.net/mftang/article/details/135882692

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值