T40 + sd2010 外置rtc
首先要确认硬件是否正常链接,设备地址是多少,可以借助i2c-tools
注意:要使用i2c-tools 内核需要打开 CONFIG_I2C_CHARDEV (用户空间支持使用i2c总线),/dev/下有i2c-*说明配置已打开
i2c-tools这个是专门用来调试i2c的
编译移植:
下载地址:
Index of /pub/software/utils/i2c-tools/
这里用的是i2c-tools-3.1.1
解压之后进入目录修改Makefile(加上静态编译 和 安装路径)
make CC=mips-linux-uclibc-gnu-gcc; make install
编译成功后生成的 _install/sbin/下就是生成的工具
使用方法:
i2cdetect:用于扫描 i2c 总线上的设备,并显示地址 i2cset:向i2c设备某个寄存器写入值 i2cget:读取i2c设备某个寄存器的值 i2cdump:读取某个i2c设备所有寄存器的值 i2ctransfer:一次性读写多个字节
可以看到从机的地址为0x32(i2c总线号问硬件工程师即可)
如果扫描不到,可能是管教复用没打开 gpio 复用i2c
不同厂商的复用方法可能不一样,具体要咨询一下厂商。
T40的复用方法
查原理图确认RTC SDA/SCK 对应的gpio为 PB20 PB21,总线是3.
内核目录 arch/mips/boot/dts/ingenic
这里不知道是怎么个规则, 0、1、2轮流试一下。
读写rtc查询规格书可知寄存器列表
方法1 用户空间直接操作 i2c设备:前面已知总线号3,地址0x32
#include方法2 加载rtc驱动 后台通过hwclock 读写时间:#include #include #include #include #include #include #include #include #include typedef struct _RJ_TIME_ { unsigned char sec; ///< 秒 unsigned char min; ///< 分 unsigned char hour; ///< 时 unsigned char wday; ///< 星期几 unsigned char mday; ///< 某日 unsigned char mon; ///< 某月 unsigned short year; ///< 某年 int msec; ///< 毫秒 }_RJ_TIME_; static char nChipAddress = (char)0x32; int readEx(int m_nFd, unsigned char p_Address, char p_RegNo, char* p_Value, int p_valueLen) { if (ioctl(m_nFd, I2C_SLAVE_FORCE, p_Address)) { printf("readEx wrong slave address!n"); return -1; } if (write(m_nFd, &p_RegNo, 1) < 0) { printf("write i2c error! n"); return -1; } if (read(m_nFd, p_Value, p_valueLen) < 0) { printf("read i2c error! n"); return -1; } return 0; } int rtc_get_time(_RJ_TIME_ *time) { int ret; unsigned char nValue[7]={0}; int m_nFd = open("/dev/i2c-3", O_RDWR); if (m_nFd <= 0) { printf("error open i2c Device failed!n"); return -1; } ret = readEx(m_nFd, nChipAddress, 0, (char*)nValue,sizeof(nValue)); if (0 != ret) { close(m_nFd); return ret; } //printf("%x,%x,%x,%x,%x,%x,%xn",nValue[0],nValue[1],nValue[2],nValue[3],nValue[4],nValue[5],nValue[6]); time->sec = (nValue[0]>>4) * 10 + (nValue[0]&0x0f) ; time->min = (nValue[1]>>4) * 10 + (nValue[1]&0x0f) ; nValue[2] &= 0x7f; time->hour = (nValue[2]>>4) * 10 + (nValue[2]&0x0f) ; time->mday = (nValue[4]>>4) * 10 + (nValue[4]&0x0f) ; time->mon = (nValue[5]>>4) * 10 + (nValue[5]&0x0f); time->year = (nValue[6]>>4) * 10 + (nValue[6]&0x0f); time->wday = (nValue[3]>>4) * 10 + (nValue[3]&0x0f) ; close(m_nFd); return 0; } int rtc_write(int m_nFd, unsigned char p_Address, char p_RegNo, char p_Value) { if (ioctl(m_nFd, I2C_SLAVE_FORCE, p_Address)) { printf("write wrong slave address(0x%x)!n",p_Address); return -1; } char mbuf[2]; mbuf[0] = p_RegNo; mbuf[1] = p_Value; if (write(m_nFd, mbuf, 2) < 0) { printf("write i2c errorn"); return -1; } return 0; } int writeEx(int m_nFd, unsigned char p_Address, char p_RegNo, char* p_Value, int p_valueLen) { if (ioctl(m_nFd, I2C_SLAVE_FORCE, p_Address)) { printf("writeEx wrong slave address!n"); return -1; } unsigned char* mbuf = (unsigned char*)calloc(p_valueLen + 1, sizeof(unsigned char)); if ( mbuf == NULL ) { printf("Error: read buffer allocationn"); return -1; } mbuf[0] = p_RegNo; for ( int i = 0; i < p_valueLen; i++ ) { mbuf[i + 1] = (unsigned char)p_Value[i]; } if (write(m_nFd, mbuf, p_valueLen + 1) < 0) { printf("write i2c error !n"); free(mbuf); return -1; } free(mbuf); return 0; } int rtc_set_time(_RJ_TIME_ *p_tmTime) { int ret; unsigned char nValue[7]={0}; int m_nFd = open("/dev/i2c-3", O_RDWR); if (m_nFd <= 0) { printf("error open i2c Device failed!n"); return -1; } unsigned char nSeconds = p_tmTime->sec; unsigned char nMinutes = p_tmTime->min; unsigned char nHours = p_tmTime->hour; unsigned char nDays = p_tmTime->mday; unsigned char nMonths = p_tmTime->mon; unsigned char nYears = p_tmTime->year; unsigned char nWeekDay = p_tmTime->wday; nValue[0] = ((nSeconds/10)<<4)|(nSeconds%10); nValue[1] = ((nMinutes/10)<<4)|(nMinutes%10); nValue[2] = ((nHours/10)<<4)|(nHours%10); nValue[2] |= 0x80;//24 小时制 nValue[3] = ((nWeekDay/10)<<4)|(nWeekDay%10); nValue[4] = ((nDays/10)<<4)|(nDays%10); nValue[5] = ((nMonths/10)<<4)|(nMonths%10); nValue[6] = ((nYears/10)<<4)|(nYears%10); rtc_write(m_nFd, nChipAddress, 0x10,0x80); rtc_write(m_nFd, nChipAddress, 0x0f,0x84); printf("nYears[%d]%x, nMonths[%d]%x, nDays[%d]%x, nWeekDay[%d]%x, nHours[%d]%x, nMinutes[%d]%x, nSeconds[%d]%xn", nYears, nValue[6], nMonths, nValue[5], nDays, nValue[4], nWeekDay, nValue[3], nHours, nValue[2], nMinutes, nValue[1], nSeconds, nValue[0]); writeEx(m_nFd, nChipAddress, 0, (char*)nValue,sizeof(nValue)); rtc_write(m_nFd, nChipAddress, 0x0f,0x0); rtc_write(m_nFd, nChipAddress, 0x10,0x0); close(m_nFd); return 0; } void case_get_rtc() { _RJ_TIME_ rtc_time; rtc_get_time(&rtc_time); printf("%d-%02d-%02d %d:%d:%dn",rtc_time.year + 2000,rtc_time.mon,rtc_time.mday, rtc_time.hour,rtc_time.min,rtc_time.sec); } void case_set_rtc() { time_t timep; struct tm *p; time(&timep); p=gmtime(&timep); printf("%d",p->tm_year + 1900); printf("-%02d",1+p->tm_mon); printf("-%02d ",p->tm_mday); printf("%02d-",p->tm_hour); printf("%02d-",p->tm_min); printf("%02d ",p->tm_sec); printf("星期 %dn",p->tm_wday); _RJ_TIME_ rtc_time; rtc_time.year = p->tm_year -100; // + 1900 -2000 if (p->tm_year +1900 < 2000) rtc_time.year = 0x0; rtc_time.mon = p->tm_mon + 1; rtc_time.mday = p->tm_mday; rtc_time.hour = p->tm_hour; rtc_time.min = p->tm_min; rtc_time.sec = p->tm_sec; rtc_time.wday = p->tm_wday; rtc_set_time(&rtc_time); } int main(int argc, char *argv[]) { int result; opterr = 0;//使getopt不行stderr输出错误信息 while( (result = getopt(argc, argv, "rwh")) != -1 ) { switch(result) { case 'r': //printf("[rtc] Get hw time.n"); case_get_rtc(); break; case 'w': printf("[rtc] Set hw time.n"); case_set_rtc(); break; case 'h': printf("[Usage: hwclock [-r|--show][-w|--systohc]n"); printf("t-r Show hardware clock timen"); printf("t-w Set hardware clock from system timen"); case '?': printf("[rtc] Unknow cmd.n"); break; default: printf("[rtc] error.n"); break; } } return 0; }
内核 drivers/rtc目录下有很多厂商已经实现好的驱动, 找一下有没有对应的型号,如果刚好没有,那么我们就自己写驱动。
如果对驱动完全一窍不通,先看下这位博主的系列文章
(一)初识Linux驱动_hanp_linux的博客-CSDN博客 (bing.com)
驱动编译的时候如果有报错 undefine 大概率是内核对应的配置没有打开,此时虽然能生成ko,但是加载的时候是加载不上去的。
我自己遇到了rtc_device_register undefine 。原因是CONFIG_RTC_CLASS没有打开
这篇文章写得已经很详细了
(2条消息) Linux驱动开发: Linux下RTC实时时钟驱动_DS小龙哥的博客-CSDN博客_rtc驱动
补充自己写得针对sd2010的rtc驱动
rtc相关操作接口遇到问题可以先尝试搜一下 rtc 厂商的官网,看有没有一些错误解释 或者demo
device端rtc_device.c
#include "linux/module.h" #include "linux/init.h" #include//释放平台总线 static void pdev_release(struct device *dev) { printk("rtc_pdev:the rtc_pdev is close!!!n"); } struct platform_device rtc_pdev= { .name = "sd2010rtc", .id = -1, .dev = { .release = pdev_release, }, }; static int __init plat_dev_init(void) { printk("rtc device initn"); platform_device_register(&rtc_pdev); return 0; } static void __exit plat_dev_exit(void) { platform_device_unregister(&rtc_pdev); } module_init(plat_dev_init); module_exit(plat_dev_exit); MODULE_LICENSE("GPL");
Makefile
CROSS_COMPILE ?= mips-linux-gnu-
ISVP_ENV_KERNEL_DIR = $(PWD)/../../../../kernel-4.4.94
KDIR := ${ISVP_ENV_KERNEL_DIR}
MODULE_NAME := rtc_device_sd2010
all: modules
.PHONY: modules clean
$(MODULE_NAME)-objs := rtc_device.o
obj-m := $(MODULE_NAME).o
modules:
@$(MAKE) -C $(KDIR) M=$(shell pwd) $@
clean:
@rm -rf *.o *~ .depend .*.cmd *.mod.c .tmp_versions *.ko *.symvers modules.order
drver端 (设置接口未完成)
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define sd2010_sec_add 0x00 //秒数据地址 #define sd2010_min_add 0x01 //分数据地址 #define sd2010_hr_add 0x02 //时数据地址 #define sd2010_day_add 0x03 //星期数据地址 #define sd2010_date_add 0x04 //日数据地址 #define sd2010_month_add 0x05 //月数据地址 #define sd2010_year_add 0x06 //年数据地址 #define REG_CTRL_1 0x0F #define REG_CTRL_2 0x10 #define REG_CTRL_3 0x11 #define RTC_I2C_NUM 3 #define RTC_ADDR 0x32 #define I2C_WRITE 0 #define I2C_READ 1 static struct i2c_board_info sd2010_info = { I2C_BOARD_INFO("sd2010", RTC_ADDR), }; static struct i2c_client *sd2010_client; static int sd2010_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) { printk("从RTC底层获取时间成功!n"); //struct i2c_client *client = to_i2c_client(dev); unsigned char addr = sd2010_sec_add; unsigned char nValue[7] = {0}; struct i2c_msg msgs[1] = { [0] = { .addr = sd2010_client->addr, .flags = I2C_READ, .len = 7, .buf = nValue, }, }; if ((i2c_transfer(sd2010_client->adapter, &msgs[0], 1)) != 1) { dev_err(&sd2010_client->dev, "%s: read errorn", __func__); return -EIO; } printk("%x,%x,%x,%x,%x,%x,%xn",nValue[0],nValue[1],nValue[2],nValue[3],nValue[4],nValue[5],nValue[6]); rtc_tm->tm_year=2018-1900; //年 rtc_tm->tm_mon=8-1; //月 rtc_tm->tm_mday=18; //日 rtc_tm->tm_hour=18; //时 rtc_tm->tm_min=18; //分 rtc_tm->tm_sec=18;//秒 rtc_tm->tm_sec = (nValue[0]>>4) * 10 + (nValue[0]&0x0f) ; rtc_tm->tm_min = (nValue[1]>>4) * 10 + (nValue[1]&0x0f) ; nValue[2] &= 0x7f; rtc_tm->tm_hour = (nValue[2]>>4) * 10 + (nValue[2]&0x0f) ; rtc_tm->tm_mday = (nValue[4]>>4) * 10 + (nValue[4]&0x0f) ; rtc_tm->tm_mon = (nValue[5]>>4) * 10 + (nValue[5]&0x0f); rtc_tm->tm_year = (nValue[6]>>4) * 10 + (nValue[6]&0x0f) + 100; return 0; } static int sd2010_rtc_settime(struct device *dev, struct rtc_time *tm) { printk("RTC收到的时间为:%d-%d-%d %d-%d-%dn",1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); //struct i2c_client *client = to_i2c_client(dev); //printk("-------------1n"); unsigned char bufdata[8] = {0}; unsigned char nSeconds = 10; unsigned char nMinutes = 10; unsigned char nHours = 10; unsigned char nDays = 10; unsigned char nMonths = 10; unsigned char nYears = 10; bufdata[0] = sd2010_sec_add; bufdata[1] = ((nSeconds/10)<<4)|(nSeconds%10); bufdata[2] = ((nMinutes/10)<<4)|(nMinutes%10); bufdata[3] = ((nHours/10)<<4)|(nHours%10); bufdata[5] |= 0x80;//24 小时制 //nValue[4] = ((nWeekDay/10)<<4)|(nWeekDay%10); bufdata[5] = ((nDays/10)<<4)|(nDays%10); bufdata[6] = ((nMonths/10)<<4)|(nMonths%10); bufdata[7] = ((nYears/10)<<4)|(nYears%10); //printk("-------------2n"); unsigned char bufoff1[2] = {0}; unsigned char bufoff2[2] = {0}; bufoff1[0] = REG_CTRL_1; bufoff1[1] = 0x0; bufoff2[0] = REG_CTRL_2; bufoff2[1] = 0x0; printk("-------------3n"); return 0; } static int sd2010_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) { alrm->enabled=0; //默认闹钟处于关闭状态 alrm->time.tm_year=2018-1900; //年 alrm->time.tm_mon=8-1; //月 alrm->time.tm_mday=18; //日 alrm->time.tm_hour=18; //时 alrm->time.tm_min=18; //分 alrm->time.tm_sec=18; //秒 printk("从RTC底层获取闹钟时间成功!n"); return 0; } static int sd2010_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { printk("RTC闹钟设置成功n"); return 0; } static int sd2010_rtc_proc(struct device *dev, struct seq_file *seq) { printk("proc调用成功n"); return 0; } static int sd2010_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { printk("alarm_irq_enable调用成功n"); return 0; } static int sd2010_rtc_ioctl(struct device *dev, unsigned int cmd,unsigned long arg) { printk("ioctl调用成功n"); return 0; } static const struct rtc_class_ops sd2010_rtcops = { .read_time = sd2010_rtc_gettime, .set_time = sd2010_rtc_settime, .read_alarm = sd2010_rtc_getalarm, .set_alarm = sd2010_rtc_setalarm, .proc = sd2010_rtc_proc, .alarm_irq_enable = sd2010_rtc_alarm_irq_enable, .ioctl = sd2010_rtc_ioctl, }; struct rtc_device *rtc; static int drv_probe(struct platform_device *pdev) { struct i2c_adapter *i2c_adap; i2c_adap = i2c_get_adapter(RTC_I2C_NUM); sd2010_client = i2c_new_device(i2c_adap, &sd2010_info); i2c_put_adapter(i2c_adap); unsigned char buf[2]; struct i2c_msg msg = { RTC_ADDR, I2C_WRITE, 2, buf, }; buf[0] = REG_CTRL_1; buf[1] = 0; #if 0 if ((i2c_transfer(sd2010_client->adapter, &msg, 1)) != 1) { dev_err(&sd2010_client->dev, "%s: write errorn", __func__); printk("i2c_transfer faild!!!n"); return -EIO; } #endif rtc = rtc_device_register("sd2010rtc",&pdev->dev, &sd2010_rtcops,THIS_MODULE); if(rtc==NULL) printk("RTC驱动注册失败n"); else printk("RTC驱动注册成功n"); return 0; } static int drv_remove(struct platform_device *dev) { rtc_device_unregister(rtc); printk("RTC驱动卸载成功n"); return 0; } struct platform_driver drv= { .probe = drv_probe, .remove = drv_remove, .driver = { .name = "sd2010rtc", }, }; static int __init plat_drv_init(void) { printk("rtc_drver init !!!n"); platform_driver_register(&drv); return 0; } static void __exit plat_drv_exit(void) { platform_driver_unregister(&drv); } module_init(plat_drv_init); module_exit(plat_drv_exit); MODULE_AUTHOR("SHJ"); MODULE_LICENSE("GPL");
Makefile
CROSS_COMPILE ?= mips-linux-gnu-
ISVP_ENV_KERNEL_DIR = $(PWD)/../../../../kernel-4.4.94
KDIR := ${ISVP_ENV_KERNEL_DIR}
MODULE_NAME := rtc_drver_sd2010
all: modules
.PHONY: modules clean
$(MODULE_NAME)-objs := rtc_drver.o
obj-m := $(MODULE_NAME).o
modules:
@$(MAKE) -C $(KDIR) M=$(shell pwd) $@
clean:
@rm -rf *.o *~ .depend .*.cmd *.mod.c .tmp_versions *.ko *.symvers modules.order



