文章目录
- 前言
- 一、硬件连接
- 二、代码移植
- 1.驱动代码
- 2.编译程序
- 三、移植到开发板
- 参考连接
前言
提示:基于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