建设网站费用预算,seo3分子的立体构型,桂林网站优化价格,九江做网站哪家公司好目录 概述
1 认识DS1302
1.1 DS1302 硬件电路
1.2 操作DS1302
1.3 注意要点
2 IO引脚位置
3 添加驱动节点
3.1 更新内核.dts
3.2 更新板卡.dtb
4 驱动程序实现
4.1 编写驱动程序
4.2 编写驱动程序的Makefile
4.3 安装驱动程序
5 验证驱动程序
5.1 编写测试程序…目录 概述
1 认识DS1302
1.1 DS1302 硬件电路
1.2 操作DS1302
1.3 注意要点
2 IO引脚位置
3 添加驱动节点
3.1 更新内核.dts
3.2 更新板卡.dtb
4 驱动程序实现
4.1 编写驱动程序
4.2 编写驱动程序的Makefile
4.3 安装驱动程序
5 验证驱动程序
5.1 编写测试程序
5.2 编写测试程序代码Makefile
5.3 运行测试App
6 实时波形分析 概述 本文介绍在platform-tree框架下如何实现复杂总线驱动程序以DS1302为例详细介绍如何在linux内核中添加driver tree节点以及如何在驱动程序中调用多线接口IO。
1 认识DS1302
DS1302 数据手册和产品信息 | 亚德诺ADI半导体 (analog.com) DS1302是一款使用非常普遍的实时时钟芯片可提供年月日时分秒week实时数据。其和MCU直接的电路也非常简单只需3个引脚CE, IO , CLK)。
主要特性
完全管理所有计时功能 实时时钟可为秒、分、小时、日期、月、星期和年计数闰年补偿有效期至2100年31 x 8电池供电通用RAM通过简单的串行端口与大多数微控制器进行接口 简单的3线接口TTL兼容(VCC 5V)用于读取或写入时钟或RAM数据的单字节或多字节突发模式数据传输低功耗运行可延长备用电池运行时间 2.0V至5.5V全面运行2.0V时电流消耗小于300nA8引脚DIP和8引脚SO封装充分减少了所需空间可选工业温度范围-40°C至85°C支持在多种应用中工作
1.1 DS1302 硬件电路
CE: 使能引脚
IO: 数据引脚读/写数据
SCLK: 时钟引脚 1.2 操作DS1302
读寄存器波形如下 CE: 高电平有效
写地址时CLK上升沿有效
读数据时CLK下降沿有效 写寄存器波形 CE: 高电平有效
写地址时CLK上升沿有效
写数据时CLK上升沿有效
1.3 注意要点
从DS1302中读取的时间数据位BCD码所以在实际运用时需要将其转化为十进制例如
// 从寄存器中读出的值为 0x14,使用时需要将其转化为14方法如下
static unsigned char bcd_2_dem(unsigned char x)
{return (x4)*10(x0x0f); //高4位乘以10再加上低4位即得到数值
}
初始化DS1302寄存器时要进行上述数据转换的逆操作方法如下
// 如果要配置分钟数为25分钟写到寄存器的值应该是 0x25。转换方法如下unsigned char dem_2_bcd( unsigned char val )
{return (((val/10) 0x0f)4)|((val%10)0x0f);
}2 IO引脚位置
DS1302芯片在测试底板上的IO引脚位置
//GPIO4_24: DS1302_CE
//GPIO4_26: DS1302_IO
//GPIO4_28: DS1302_CLKCE_1302 P2^4; ----- D3 -- GPIO4_24
IO_1302 P2^3; ----- D5 -- GPIO4_26
CLK_1302 P2^2; ----- D7 -- GPIO4_28
硬件实物图 在板卡ATK-DL6Y2C上DS1302的对应接口 3 添加驱动节点
3.1 更新内核.dts
DS1302引脚和IMX.6ULL引脚对应关系
GPIO4_24: DS1302_CE
GPIO4_26: DS1302_IO
GPIO4_28: DS1302_CLK
.dts文件路径
/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c/arch/arm/boot/dts/imx6ull-14x14-evk.dts
1 使用 i.MX Pins Tool v6 配置IO Pin 2) 添加IOMUXC数据至.dts文件 3添加设备compatible至.dts文件
代码信息 //mftang: users ds1302, 2024-1-31//GPIO4_24: DS1302_CE//GPIO4_26: DS1302_IO//GPIO4_28: DS1302_CLKmftangds1302 {compatible atk-dl6y2c,ds1302;pinctrl-names default;pinctrl-0 pinctrl_gpio_mftangds1302;ce-gpios gpio4 24 GPIO_ACTIVE_HIGH;io-gpios gpio4 26 GPIO_ACTIVE_HIGH;clk-gpios gpio4 28 GPIO_ACTIVE_HIGH;status okay;};
4) 编译.dts文件
在内核根目录下使用
make dtbs 5 复制 .dtb 文件至NFS共享目录
cp arch/arm/boot/dts/imx6ull-14x14-emmc-4.3-480x272-c.dtb /home/mftang/nfs/atk_dl6y2c/ 3.2 更新板卡.dtb
开发版中的.dtb文件存放位置
cd /run/media/mmcblk1p1 在开发板上把 .dtb文件复制到应用目录中
cp /mnt/atk_dl6y2c/imx6ull-14x14-emmc-4.3-480x272-c.dtb /run/media/mmcblk1p1
复制.dtb文件到相应的运行目录然后重新板卡。在/proc/device-tree中可以看见device节点然后可以在driver中使用该节点。 4 驱动程序实现
4.1 编写驱动程序
驱动程序源码
/***************************************************************
Copyright 2024-2029. All rights reserved.
文件名 : drv_09_tree_hs0038.c
作者 : tangmingfei2013126.com
版本 : V1.0
描述 : ds1302 驱动程序
其他 : 无
日志 : 初版V1.0 2024/02/01 使用方法
1) 在.dts文件中定义节点信息//mftang: users ds1302, 2024-1-31//GPIO4_24: DS1302_CE//GPIO4_26: DS1302_IO//GPIO4_28: DS1302_CLKmftangds1302 {compatible atk-dl6y2c,ds1302;pinctrl-names default;pinctrl-0 pinctrl_gpio_mftangds1302;gpios-ce gpio4 24 GPIO_ACTIVE_HIGH;gpios-io gpio4 26 GPIO_ACTIVE_HIGH;gpios-clk gpio4 28 GPIO_ACTIVE_HIGH;status okay;};2) 在驱动匹配列表
static const struct of_device_id ds1302_of_match[] {{ .compatible atk-dl6y2c,ds1302 },{ } // Sentinel
};3) 驱动使用方法
typedef struct{unsigned char second;unsigned char minute;unsigned char hour;unsigned char week;unsigned char day;unsigned char month;unsigned char year;
}stru_ds1302_rtc;stru_ds1302_rtc rtc;read(fd, rtc, sizeof(stru_ds1302_rtc));***************************************************************/
#include linux/module.h
#include linux/poll.h#include linux/fs.h
#include linux/errno.h
#include linux/miscdevice.h
#include linux/kernel.h
#include linux/major.h
#include linux/mutex.h
#include linux/proc_fs.h
#include linux/seq_file.h
#include linux/stat.h
#include linux/init.h
#include linux/device.h
#include linux/tty.h
#include linux/kmod.h
#include linux/gfp.h
#include linux/gpio/consumer.h
#include linux/platform_device.h
#include linux/of_gpio.h
#include linux/of_irq.h
#include linux/interrupt.h
#include linux/irq.h
#include linux/slab.h
#include linux/fcntl.h
#include linux/timer.h
#include linux/workqueue.h
#include asm/current.h#define DEVICE_NAME treeds1302 // dev/treeds1302/* ds1302dev设备结构体 */
struct ds1302stru_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */struct device_node *node; /* ds1302设备节点 */int userds1302; /* ds1302 GPIO标号 */struct gpio_desc *pin_ce;struct gpio_desc *pin_io;struct gpio_desc *pin_clk;
};struct ds1302stru_dev ds1302dev; /* ds1302设备 */
static wait_queue_head_t ds1302_wq;static const unsigned char RTC_REG[7] {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};/*device 相关的驱动程序
*/
static unsigned char bcd_2_dem(unsigned char x)
{return (x4)*10(x0x0f); //高4位乘以10再加上低4位即得到数值
}static void ds1302_wr_byte(unsigned char dat) //DS1302写入操作
{unsigned char i;for(i0;i8;i){if(dat0x01){ //从低字节开始传送gpiod_direction_output(ds1302dev.pin_io, 1); // ds1302 io 1}else {gpiod_direction_output(ds1302dev.pin_io, 0); // ds1302 io 0}// CLK_13020; gpiod_direction_output(ds1302dev.pin_clk, 0);// CLK_13021;gpiod_direction_output(ds1302dev.pin_clk, 1);dat dat1;}
}static unsigned char ds1302_rd_byte(void) //DS1302读取操作
{unsigned char i,temp 0;// IO_1302 as inputgpiod_direction_input( ds1302dev.pin_io );for(i0;i8;i){if( gpiod_get_value(ds1302dev.pin_io) )temptemp|0x80;elsetemptemp0x7f;// CLK_1302 1gpiod_direction_output(ds1302dev.pin_clk, 1);// CLK_1302 0gpiod_direction_output(ds1302dev.pin_clk, 0);temptemp1;}return(temp);
}static void write_ds1302_reg(unsigned char addr,unsigned char dat)
{unsigned long flags;local_irq_save(flags);//CLK_13020;gpiod_direction_output(ds1302dev.pin_clk,0);//CE_13021; gpiod_direction_output(ds1302dev.pin_ce, 1);ds1302_wr_byte(addr); ds1302_wr_byte(dat);//CE_13020;gpiod_direction_output(ds1302dev.pin_ce, 0);//CLK_13020;gpiod_direction_output(ds1302dev.pin_clk,0);local_irq_restore(flags);
}static unsigned char read_ds1302_reg(unsigned char addr)
{unsigned long flags;unsigned char temp;local_irq_save(flags);// CLK_13020gpiod_direction_output(ds1302dev.pin_clk, 0);// CE_13021 gpiod_direction_output(ds1302dev.pin_ce, 1);ds1302_wr_byte(addr); //写入地址temp ds1302_rd_byte();// CE_13020gpiod_direction_output(ds1302dev.pin_ce, 0);// CLK_13020gpiod_direction_output(ds1302dev.pin_clk, 0);local_irq_restore(flags);return(temp);
} static void ds1302_wr_wp(unsigned char wp)
{if (wp)write_ds1302_reg(0x8e,0x80);elsewrite_ds1302_reg(0x8e,0x00);
}static void ds1302_stop(unsigned char flag)
{unsigned char chold;chold read_ds1302_reg(0x81);if (flag)write_ds1302_reg(0x80,chold|0x80);elsewrite_ds1302_reg(0x80,chold0x7f);
}static unsigned char ds1302_read_rtc( unsigned char reg )
{unsigned char dat;dat read_ds1302_reg(reg);return bcd_2_dem(dat);
}static void ds1302_get_rtc( unsigned char *buff)
{int LEN sizeof(RTC_REG);int i 0;for( i 0; i LEN; i ){buff[i] ds1302_read_rtc( RTC_REG[i]|0x01);}
}static void ds1302_drv_init( unsigned char *buff )
{unsigned long flags;unsigned char temp,val;int LEN sizeof(RTC_REG);int i;ds1302_stop(1); // stop clockds1302_wr_wp(0); // enable write local_irq_save(flags);for ( i0; i LEN; i){val buff[i];temp (((val/10) 0x0f)4)|((val%10)0x0f);write_ds1302_reg( RTC_REG[i], temp );}local_irq_restore(flags);ds1302_wr_wp(1); // disable write ds1302_stop(0); // enable clock
}/*linux driver 驱动接口 实现对应的open/read/write等函数填入file_operations结构体
*/
static ssize_t ds1302_drv_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int LEN sizeof(RTC_REG);unsigned char tempbuff[LEN];int length;length copy_from_user(tempbuff, buf, LEN);if( cnt ! LEN ){printk( %s line %d write ds1302 register error! \r\n, __FUNCTION__, __LINE__);return 0;}else{ds1302_drv_init( tempbuff );}return cnt;
}static ssize_t ds1302_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int LEN sizeof(RTC_REG);unsigned char tempbuff[LEN];int length;ds1302_get_rtc( tempbuff );length copy_to_user(buf, tempbuff, LEN);return length;
}static unsigned int ds1302_drv_poll(struct file *fp, poll_table * wait)
{printk( %s line %d \r\n, __FUNCTION__, __LINE__);return 0;
}static int ds1302_drv_close(struct inode *node, struct file *file)
{printk( %s line %d \r\n, __FUNCTION__, __LINE__);return 0;
}/* 定义driver的file_operations结构体
*/
static struct file_operations ds1302_fops {.owner THIS_MODULE,.write ds1302_drv_write,.read ds1302_drv_read,.poll ds1302_drv_poll,.release ds1302_drv_close,
};/* 1. 从platform_device获得GPIOmftangds1302 {compatible atk-dl6y2c,ds1302;pinctrl-names default;pinctrl-0 pinctrl_gpio_mftangds1302;ce-gpios gpio4 24 GPIO_ACTIVE_HIGH;io-gpios gpio4 26 GPIO_ACTIVE_HIGH;clk-gpios gpio4 28 GPIO_ACTIVE_HIGH;status okay;};*/
static int ds1302_probe(struct platform_device *pdev)
{printk(ds0302 driver and device was matched!\r\n);/* 1. 获得硬件信息 */ds1302dev.pin_ce gpiod_get(pdev-dev, ce, 0);if (IS_ERR(ds1302dev.pin_ce)){printk(%s line %d get ce parameter error! \n, __FUNCTION__, __LINE__);}ds1302dev.pin_io gpiod_get(pdev-dev, io, 0);if (IS_ERR(ds1302dev.pin_io)){printk(%s line %d get io parameter error! \n, __FUNCTION__, __LINE__);}ds1302dev.pin_clk gpiod_get(pdev-dev, clk, 0);if (IS_ERR(ds1302dev.pin_clk)){printk(%s line %d get clk parameter error! \n, __FUNCTION__, __LINE__);}/* 2. device_create */device_create( ds1302dev.class, NULL, MKDEV( ds1302dev.major, 0 ), NULL, DEVICE_NAME); return 0;
}static int ds1302_remove(struct platform_device *pdev)
{device_destroy( ds1302dev.class, MKDEV( ds1302dev.major, 0));gpiod_put(ds1302dev.pin_ce);gpiod_put(ds1302dev.pin_io);gpiod_put(ds1302dev.pin_clk);return 0;
}static const struct of_device_id atk_dl6y2c_ds1302[] {{ .compatible atk-dl6y2c,ds1302 },{ },
};/* 1. 定义platform_driver */
static struct platform_driver ds1302_pltdrv {.probe ds1302_probe,.remove ds1302_remove,.driver {.name atk_ds1302,.of_match_table atk_dl6y2c_ds1302,},
};/* 2. 在入口函数注册platform_driver */
static int __init ds1302_init(void)
{printk(%s line %d\n,__FUNCTION__, __LINE__);/* register file_operations */ds1302dev.major register_chrdev( 0, DEVICE_NAME, /* device name */ds1302_fops); /* create the device class */ds1302dev.class class_create(THIS_MODULE, ds1302_class);if (IS_ERR(ds1302dev.class)) {printk(%s line %d\n, __FUNCTION__, __LINE__);unregister_chrdev( ds1302dev.major, DEVICE_NAME);return PTR_ERR( ds1302dev.class );}init_waitqueue_head(ds1302_wq);return platform_driver_register(ds1302_pltdrv);
}/* 3. 有入口函数就应该有出口函数卸载驱动程序时就会去调用这个出口函数* 卸载platform_driver*/
static void __exit ds1302_exit(void)
{printk(%s line %d\n, __FUNCTION__, __LINE__);platform_driver_unregister(ds1302_pltdrv);class_destroy(ds1302dev.class);unregister_chrdev(ds1302dev.major, DEVICE_NAME);
}/* 7. 其他完善提供设备信息自动创建设备节*/module_init(ds1302_init);
module_exit(ds1302_exit);MODULE_LICENSE(GPL);
MODULE_AUTHOR(tangmingfei2013126.com);
4.2 编写驱动程序的Makefile
PWD : $(shell pwd)KERNEL_DIR/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c
ARCHarm
CROSS_COMPILE/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-export ARCH CROSS_COMPILEobj-m: drv_10_tree_ds1302.oall:$(MAKE) -C $(KERNEL_DIR) M$(PWD) modulesclean:rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.order *.symvers
4.3 安装驱动程序
在dev/目录下查看驱动程序 5 验证驱动程序
5.1 编写测试程序
测试程序源码
/***************************************************************
Copyright 2024-2029. All rights reserved.
文件名 : test_10_tree_ds1302.c
作者 : tangmingfei2013126.com
版本 : V1.0
描述 : ds1302 测试程序,用于测试 drv_10_tree_ds1302
日志 : 初版V1.0 2024/1/29***************************************************************/
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include stdio.h
#include string.h#define DEV_NAME /dev/treeds1302typedef struct{unsigned char second;unsigned char minute;unsigned char hour;unsigned char week;unsigned char day;unsigned char month;unsigned char year;
}stru_ds1302_rtc;stru_ds1302_rtc rtc;int main(int argc, char **argv)
{int fd;fd open(DEV_NAME, O_RDWR);if (fd 0){printf(can not open file %s \r\n, DEV_NAME);return -1;}// init rtc rtc.year 24;rtc.month 2;rtc.day 1;rtc.week 4;rtc.hour 18;rtc.minute 2;rtc.second 0;write(fd, rtc, sizeof(stru_ds1302_rtc));while(1){read(fd, rtc, sizeof(stru_ds1302_rtc));printf( %02d-%02d-%02d week %d %02d:%02d:%02d \r\n, rtc.year, rtc.month, rtc.day, rtc.week,rtc.hour,rtc.minute, rtc.second);sleep(1);}close(fd);return 0;
}
5.2 编写测试程序代码Makefile
CFLAGS -Wall -O2
CC/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
STRIP/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-striptest_10_tree_ds1302: test_10_tree_ds1302.o$(CC) $(CFLAGS) -o test_10_tree_ds1302 test_10_tree_ds1302.o$(STRIP) -s test_10_tree_ds1302clean:rm -f test_10_tree_ds1302 test_10_tree_ds1302.o
5.3 运行测试App
运行测试程序后系统会初始化DS1302的时间然后每隔1s从芯片中读取时间 6 实时波形分析
分析一个简单的波形从寄存器0x81中读取秒数据秒数为57具体波形图如下 读一个完整的年月日时分秒波形