按键驱动加RTC

该博客详细介绍了如何在Linux内核中实现RTC(实时时钟)DS3231的驱动程序,包括读取和设置时间、I2C通信以及中断处理。通过GPIO进行中断触发,并展示了相关的寄存器定义和中断服务例程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <asm/delay.h>
#include <linux/delay.h>


void __iomem* reg_gpio_base_va = 0;
#define GPIO_ADRESS_BASE         0x12140000
#define GPIO_CTRL_ADRESS_BASE    0x12040000


#define HI_IO_GPIO_ADDRESS(x)  (reg_gpio_base_va + ((x)-(GPIO_ADRESS_BASE)))
#define GPIO9_REG6       HI_IO_GPIO_ADDRESS(GPIO_ADRESS_BASE+(0x9000)+0x100)
#define I2C1_CTRL_REG6   HI_IO_GPIO_ADDRESS(GPIO_CTRL_ADRESS_BASE+0x005C) 
#define I2C1_CTRL_REG7   HI_IO_GPIO_ADDRESS(GPIO_CTRL_ADRESS_BASE+0x0060)
#define GPIO9_CTRL_REG6  HI_IO_GPIO_ADDRESS(GPIO_CTRL_ADRESS_BASE+0x0134)
#define GPIO9_IS6         HI_IO_GPIO_ADDRESS(GPIO_ADRESS_BASE+(0x9000)+0x404)
#define GPIO9_IBE6         HI_IO_GPIO_ADDRESS(GPIO_ADRESS_BASE+(0x9000)+0x408)
#define GPIO9_IEV6       HI_IO_GPIO_ADDRESS(GPIO_ADRESS_BASE+(0x9000)+0x40C)
#define GPIO9_IC6        HI_IO_GPIO_ADDRESS(GPIO_ADRESS_BASE+(0x9000)+0x41C)
#define GPIO9_IE6        HI_IO_GPIO_ADDRESS(GPIO_ADRESS_BASE+(0x9000)+0x410)
#define GPIO9_DIR6         HI_IO_GPIO_ADDRESS(GPIO_ADRESS_BASE+(0x9000)+0x400)
#define GPIO_SET(g,r)           ((*(volatile unsigned int *)g) |=  (1<<(r))) 
#define GPIO_RST(g,r)           ((*(volatile unsigned int *)g) &= ~(1<<(r)))
#define  GPIO_WRITE_REG(Addr, Value)     ((*(volatile unsigned int *)(Addr))=(Value))
#define  GPIO_READ_REG(Addr)             (*(volatile unsigned int *)(Addr))


#define MODULE_VERSION_STRING    "RTC-DS3231"
#define DS3232_DRIVER_VERSION    "v1.0.0"

#define DS3231_REG_SECONDS    0x00
#define DS3231_REG_MINUTES    0x01
#define DS3231_REG_HOURS    0x02
#define DS3231_REG_AMPM        0x02
#define DS3231_REG_DAY        0x03
#define DS3231_REG_DATE        0x04
#define DS3231_REG_MONTH    0x05
#define DS3231_REG_CENTURY    0x05
#define DS3231_REG_YEAR        0x06
#define DS3231_REG_ALARM1         0x07    /* Alarm 1 BASE */
#define DS3231_REG_ALARM2         0x0B    /* Alarm 2 BASE */
#define DS3231_REG_CR        0x0E    /* Control register */
#define DS3231_REG_CR_nEOSC        0x80
#define DS3231_REG_CR_INTCN        0x04
#define DS3231_REG_CR_A2IE        0x02
#define DS3231_REG_CR_A1IE        0x01

#define DS3231_REG_SR    0x0F    /* control/status register */
#define DS3231_REG_SR_OSF   0x80
#define DS3231_REG_SR_BSY   0x04
#define DS3231_REG_SR_A2F   0x02
#define DS3231_REG_SR_A1F   0x01

#define DEVICE_NAME            "ds3231rtc"
#define I2C_ADDR_DS3231        0xD0
#ifdef DS3231_DEBUG
#define HI_MSG(x...) \
do { \
    printk("%s->%d: ", __FUNCTION__, __LINE__); \
    printk(x); \
    printk("\n"); \
} while (0)    
#else
#define HI_MSG(args...) do { } while (0)
#endif

#define HI_ERR(x...) \
do { \
    printk(KERN_ALERT "%s->%d: ", __FUNCTION__, __LINE__); \
    printk(KERN_ALERT x); \
    printk(KERN_ALERT "\n"); \
} while (0)

#ifdef DS3231_DEBUG
static void display_rtc_time_in_bcd(struct rtc_time *rtime)
{
    printk("tm_sec: %u\n", rtime->tm_sec);
    printk("tm_min: %u\n", rtime->tm_min);
    printk("tm_hour: %u\n", rtime->tm_hour);
    printk("tm_mday: %u\n", rtime->tm_mday);
    printk("tm_mon: %u\n", rtime->tm_mon);
    printk("tm_year: %u\n", rtime->tm_year);
    printk("tm_wday: %u\n", rtime->tm_wday);
    printk("tm_yday: %u\n", rtime->tm_yday);
    printk("tm_isdst: %u\n", rtime->tm_isdst);
}
#endif
struct rtc_time timess;
struct i2c_client *rtc_client;
struct rtc_device *rtc;
struct device *devicess;
int temp_flag=0;

extern int hi_i2c_dma_read(const struct i2c_client *client, unsigned int data_addr,
        unsigned int reg_addr, unsigned int reg_addr_num,
        unsigned int length);
extern int hi_i2c_dma_write(const struct i2c_client *client, unsigned int data_addr,
        unsigned int reg_addr, unsigned int reg_addr_num,
        unsigned int length);
const struct i2c_device_id ds3231_id[]=
{

{"ds3231rtc",2},
{}
};

static struct i2c_board_info i2c_ds3231rtc=
{
//参数一 名称
//参数二 从设备地址
I2C_BOARD_INFO("ds3231rtc", (I2C_ADDR_DS3231>>1)),
};
static void display_rtc_time_in_bcd(struct rtc_time *rtime)
{
    printk("tm_sec: %u\n", rtime->tm_sec);
    printk("tm_min: %u\n", rtime->tm_min);
    printk("tm_hour: %u\n", rtime->tm_hour);
    printk("tm_mday: %u\n", rtime->tm_mday);
    printk("tm_mon: %u\n", rtime->tm_mon);
    printk("tm_year: %u\n", rtime->tm_year);
    printk("tm_wday: %u\n", rtime->tm_wday);
//    printk("tm_yday: %u\n", rtime->tm_yday);
//    printk("tm_isdst: %u\n", rtime->tm_isdst);
}

static int i2cdev_read(char *buf, unsigned int count,
        unsigned int reg_addr_num, unsigned int data_byte_num)
{
    int ret;
    unsigned char tmp_buf0[4];
    unsigned char tmp_buf1[4];
    struct i2c_msg msg[2];
    int idx = 0;
    struct i2c_client * client;
    client=rtc_client;
    msg[0].addr = (I2C_ADDR_DS3231>>1);
    msg[0].flags = client->flags & I2C_M_TEN;
    msg[0].len = reg_addr_num;
    msg[0].buf = tmp_buf0;
    if(reg_addr_num == 1)
    {
        tmp_buf0[idx++] = buf[0]&0xff;
    }
    else
    {
        tmp_buf0[idx++] = (buf[0] >> 8)&0xff;
        tmp_buf0[idx++] = buf[0]&0xff;
    }
    msg[1].addr = (I2C_ADDR_DS3231>>1);
    msg[1].flags = client->flags & I2C_M_TEN;
    msg[1].flags |= I2C_M_RD;
    msg[1].len = data_byte_num;
    msg[1].buf = tmp_buf1;
    ret = i2c_transfer(client->adapter, msg, 2);
    if (ret == 2)
    {
        if (data_byte_num == 2)
        {
            buf[1] = tmp_buf1[1];
            buf[0] = tmp_buf1[0];
        }
        else
        {
            buf[0] = tmp_buf1[0];
        }
    }
    else
    {
        return 0;
    }
    return 1;
}
static int ds3231_i2c_readbyte(struct i2c_client *client, unsigned char reg_addr)
{
    int ret = 0;
    unsigned char buf[2];

    buf[0] = reg_addr;
    ret = i2cdev_read(buf, 1, 1, 1);
    if (ret < 0)
    {
        dev_err(&client->dev, "ds3231_i2c_readbyte fail...\n");
        return ret;
    }
    
    return buf[0];
}
static int ds3231_i2c_writebyte(struct i2c_client *client,unsigned char reg_addr,
    unsigned char value)
{
    int ret = 0;
    unsigned char buf[2];

    buf[0] = reg_addr;
    buf[1] = value;

    ret = i2c_master_send(client, buf, 2);
    return ret;
}

static int ds3231_i2c_dma_read(struct i2c_client *client, unsigned char reg_addr, 
        unsigned char len, unsigned char *buffer)
    {
        int ret;
        dma_addr_t dma_handle;
        unsigned char *p;
        printk("enter dma_read...................\n");
        p = dma_alloc_coherent(NULL, len, &dma_handle, GFP_KERNEL);
        if(p == NULL)
        {
            printk("dma_alloc_coherent fail\n");
            return -1;
        }
        ret = hi_i2c_dma_read(client, dma_handle, reg_addr, 1, len);
        if(ret)
            printk("dma_write fail:%d.\n", ret);
    
        memcpy(buffer, p, len);
        dma_free_coherent(NULL, len, p, dma_handle);
        return 0;
    }
static int ds3231_i2c_dma_write(struct i2c_client *client,unsigned char reg_addr,
            unsigned char len,unsigned char *buffer)
    {
            int ret;
            dma_addr_t dma_handle;
            unsigned char *p;
        
            p = dma_alloc_coherent(NULL, len, &dma_handle, GFP_KERNEL);
            if(p == NULL)
            {
                printk("dma_alloc_coherent fail\n");
                return -1;
            }
            memcpy(p, buffer, len);
            
            ret = hi_i2c_dma_write(client, dma_handle, reg_addr, 1, len);
            if(ret)
                printk("dma_write fail:%d.\n", ret);
        
            dma_free_coherent(NULL, len, p, dma_handle);
            return 0;
    }
static int ds3231_read_time(struct device *dev,struct rtc_time *time)
    {
        struct i2c_client *client = rtc_client;
        int ret;
        u8 buf[7];
        unsigned int year, month, day, hour, minute, second;
        unsigned int week, twelve_hr, am_pm;
        unsigned int century, add_century = 0;
    
        ret = ds3231_i2c_dma_read(client, DS3231_REG_SECONDS, 7, buf);
    
        if (ret < 0) {
            dev_warn(&client->dev, "ds3231_i2c_dma_read read time error!ret = %d\n", ret);
            return ret;
        }
    
        second = buf[0];
        minute = buf[1];
        hour = buf[2];
        week = buf[3];
        day = buf[4];
        month = buf[5];
        year = buf[6];
                                
        /* Extract additional information for AM/PM and century */
        twelve_hr = hour & 0x40;
        am_pm = hour & 0x20;
        century = month & 0x80;
#ifdef DS3231_DEBUG
    {
    int i;
    printk("read from rtc-ds3231m: ");
    for (i = 0; i < 7; i++) {
        printk("0x%02x ", buf[i]);
    }
    printk("\n");
                                        
    }
#endif
    /* Write to rtc_time structure */
                                
    time->tm_sec = bcd2bin(second);
    time->tm_min = bcd2bin(minute);
    if (twelve_hr) {
    /* Convert to 24 hr */
    if (am_pm)
    time->tm_hour = bcd2bin(hour & 0x1F) + 12;
    else
    time->tm_hour = bcd2bin(hour & 0x1F);
    } else {
    time->tm_hour = bcd2bin(hour);
    }
                                
    /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */
    time->tm_wday = bcd2bin(week) - 1;
    time->tm_mday = bcd2bin(day);
    /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */
    time->tm_mon = bcd2bin(month & 0x7F) - 1;
    if (century)
    add_century = 100;
                                
    time->tm_year = bcd2bin(year) + add_century;
                                
    ret = rtc_valid_tm(time); 
    if (ret < 0) {
    dev_err(&client->dev, "rtc_valid_tm return ret: %d\n", ret);
    }

    timess.tm_sec=time->tm_sec;
    timess.tm_hour=time->tm_hour;
    timess.tm_min=time->tm_min;
    timess.tm_mon=(time->tm_mon) +1;
    timess.tm_mday=time->tm_mday;
    timess.tm_wday=time->tm_wday;
    timess.tm_year=(time->tm_year)+1900;

    return ret;
}
static int ds3231_set_time(struct device *dev, struct rtc_time *time)
{
    struct i2c_client *client = to_i2c_client(dev);
    u8 buf[7];
    int ret;
    /* Extract time from rtc_time and load into ds3231*/

    buf[0] = bin2bcd(time->tm_sec);
    buf[1] = bin2bcd(time->tm_min);
    buf[2] = bin2bcd(time->tm_hour);
    /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */
    buf[3] = bin2bcd(time->tm_wday + 1);
    buf[4] = bin2bcd(time->tm_mday); /* Date */
    /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */
    buf[5] = bin2bcd(time->tm_mon + 1);
    if (time->tm_year >= 100) {
        buf[5] |= 0x80;
        buf[6] = bin2bcd(time->tm_year - 100);
    }
    else 
    {
        buf[6] = bin2bcd(time->tm_year);
    }

#ifdef DS3231_DEBUG
    {
        int i = 0;
        display_rtc_time_in_bcd(time);
        printk("write buffer: ");
        for (i = 0; i < 7; i++) {
            printk("0x%02x ", buf[i]);
        }
        printk("\n");
    }
#endif
    ret = ds3231_i2c_dma_write(client,
                          DS3231_REG_SECONDS, 7, buf);
    
    if (ret < 0) {
        dev_err(&client->dev, "ds3231_i2c_dma_write set time failed, ret: %d\n", ret);
    } else {
        /* take some sleep to fix bug read immendiately after set */
        udelay(500);
    }
    return ret;
}
static int ds3231_check_rtc_status(struct i2c_client *client)
{
    int ret = 0;
    int control, stat;

    stat = ds3231_i2c_readbyte(client, DS3231_REG_SR);
    if (stat < 0) {
        dev_err(&client->dev, "ds3231_i2c_readbyte failed!stat = 0x%08x\n", stat);
        return stat;
    }

    if (stat & DS3231_REG_SR_OSF)
        dev_warn(&client->dev,
                "oscillator discontinuity flagged, "
                "time unreliable\n");

    stat &= ~(DS3231_REG_SR_OSF | DS3231_REG_SR_A1F | DS3231_REG_SR_A2F);

    ret = ds3231_i2c_writebyte(client, DS3231_REG_SR, stat);
    if (ret < 0) {
        dev_warn(&client->dev, "ds3231_i2c_writebyte error!\n");
        return ret;
    }

    /* If the alarm is pending, clear it before requesting
     * the interrupt, so an interrupt event isn't reported
     * before everything is initialized.
     */

    control = ds3231_i2c_readbyte(client, DS3231_REG_CR);
    if (control < 0) {
        dev_warn(&client->dev, "ds3231_i2c_readbyte read control error!control = 0x%08x\n", control);
        return control;
    }

    control &= ~(DS3231_REG_CR_A1IE | DS3231_REG_CR_A2IE);
    control |= DS3231_REG_CR_INTCN;

    ret = ds3231_i2c_writebyte(client, DS3231_REG_CR, control);
    if (ret < 0) {
        dev_warn(&client->dev, "ds3231_i2c_writebyte write control error!ret = 0x%08x\n", ret);
        return control;
    }
    return ret;
}
void read_ds3231(void)
{
            
        struct i2c_client *client = rtc_client;
        int ret;
        u8 buf[7];
        struct rtc_time rtc_tm ;
        
        unsigned int year, month, day, hour, minute, second;
        unsigned int week, twelve_hr, am_pm;
        unsigned int century, add_century = 0;
        printk("enter ds3231_read\n");

        ret = ds3231_i2c_dma_read(client, DS3231_REG_SECONDS, 7, buf);

        second = buf[0];
        minute = buf[1];
        hour = buf[2];
        week = buf[3];
        day = buf[4];
        month = buf[5];
        year = buf[6];
                                
        /* Extract additional information for AM/PM and century */
        twelve_hr = hour & 0x40;
        am_pm = hour & 0x20;
        century = month & 0x80;
        printk("asdasdasadasdasd\n");
        
        /* Write to rtc_time structure */                        
        rtc_tm.tm_sec = bcd2bin(second);
        rtc_tm.tm_min = bcd2bin(minute);
        if (twelve_hr) {
        /* Convert to 24 hr */
        if (am_pm)
        rtc_tm.tm_hour = bcd2bin(hour & 0x1F) + 12;
        else
        rtc_tm.tm_hour = bcd2bin(hour & 0x1F);
        } else {
        rtc_tm.tm_hour = bcd2bin(hour);
        }
                                    
        /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */
        rtc_tm.tm_wday = bcd2bin(week) - 1;
        rtc_tm.tm_mday = bcd2bin(day);
        /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */
        rtc_tm.tm_mon = bcd2bin(month & 0x7F);
        if (century)
        add_century = 2000;
                                    
        rtc_tm.tm_year = bcd2bin(year) + add_century;
                                    
        ret = rtc_valid_tm(&rtc_tm); 
        if (ret < 0) {
        dev_err(&client->dev, "rtc_valid_tm return ret: %d\n", ret);
        }
        display_rtc_time_in_bcd(&rtc_tm);
    
}

static const struct rtc_class_ops ds3231_rtc_ops = {
    .read_time = ds3231_read_time,
    .set_time = ds3231_set_time,
//    .read_alarm = ds3231_read_alarm,
//    .set_alarm = ds3231_set_alarm,
//    .alarm_irq_enable = ds3231_alarm_irq_enable,
};
int ds3231rtc_probe(struct i2c_client *client,const struct i2c_device_id *id)
{        
        int ret;
        printk("enter ds3231_probe\n");
        ret = ds3231_check_rtc_status(client);
        if (ret < 0) {
        dev_err(&client->dev, "ds3231_check_rtc_status failed! ret = %d\n", ret);
        return ret;
        }
        rtc = devm_rtc_device_register(&client->dev, client->name,
                                  &ds3231_rtc_ops, THIS_MODULE);
        read_ds3231();
        return 0;
}
int ds3231rtc_remove(struct i2c_client *client)
{


        return 0; 
}


struct i2c_driver  ds3231_driver=
{
        .driver =
        {
        .name="ds3231rtc",
        .owner=THIS_MODULE,
                
        },
        .probe =ds3231rtc_probe,
        .remove=ds3231rtc_remove,
        /*i2c match 会使用到*/
        .id_table=ds3231_id,
};

irqreturn_t ds3231_isr(int irq,void *dev_id)
{    
    int ret;
    int times=0;

    printk("occur up key press!\n");

    GPIO_READ:
    ret=GPIO_READ_REG(GPIO9_REG6);
    if(ret==0)
    {
    times++;
    mdelay(100);
    goto GPIO_READ;
    }
    printk("times-> %d ms\n",(times));

    display_rtc_time_in_bcd(&timess);
    return IRQ_HANDLED;
}


int __init ds3231rtc_init(void)
{        
        int ret;
        struct i2c_adapter* i2c_adap;
        reg_gpio_base_va = ioremap(GPIO_ADRESS_BASE,0x100);
    //    GPIO_WRITE_REG(I2C1_CTRL_REG6, 1);//设置GPIO1_6复用
    //    GPIO_WRITE_REG(I2C1_CTRL_REG7, 1);//设置GPIO1_7复用
        GPIO_WRITE_REG(GPIO9_CTRL_REG6, 0);//设置GPIO1_7复用
        GPIO_RST(GPIO9_DIR6, 6);//按键为输出
        /*配置中断*/
        GPIO_RST(GPIO9_IS6, 6);
        GPIO_RST(GPIO9_IEV6,6);
        GPIO_RST(GPIO9_IBE6,6);
        GPIO_SET(GPIO9_IC6, 6);
        GPIO_SET(GPIO9_IE6, 6);


        /*注册中断*/
        ret=request_irq(76,ds3231_isr,IRQF_SHARED,"interrupt_demo",(void*)0x01);
        if(ret==0)
        {
        printk("success\n");
        }
        else
        {    
        printk("%d",ret);
        }

        i2c_adap   = i2c_get_adapter(2);//I2C2
        rtc_client = i2c_new_device(i2c_adap, &i2c_ds3231rtc);
        i2c_put_adapter(i2c_adap);
        i2c_add_driver(&ds3231_driver);
    
            
        return 0;
}

void __exit ds3231rtc_exit(void)
{
        iounmap(reg_gpio_base_va);
        free_irq(76,(void *)0x01);
        i2c_unregister_device(rtc_client);
        i2c_del_driver(&ds3231_driver);
        printk("ds3231 driver unregistered\n");
}
module_init(ds3231rtc_init);
module_exit(ds3231rtc_exit);
MODULE_LICENSE("GPL");


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值