Timer使用参考
1. 概述¶
公版提供3个Timer硬件定时器,可配置为单次触发或循环触发。
Timer默认是关闭的,可自行决定是否开启。
2. Timer控制¶
2.1. 设置单次/循环触发¶
#define TIMER_ENABLE (0x1) /*设置循环触发*/ #define TIMER_TRIG (0x2) /*设置单次触发*/ //二选一 WRITE_WORD(reg_base, TIMER_ENABLE|TIMER_INTERRUPT); /*设置循环触发*/ WRITE_WORD(reg_base, TIMER_TRIG|TIMER_INTERRUPT); /*设置单次触发*/
2.2. 启动定时器¶
//以下3个timer定时设为2秒,参数单位为us
_set_timer(2000000, REG_BASE_TIMER0); mdelay(50); //同时启动定时器,必须加delay _set_timer(2000000, REG_BASE_TIMER1); mdelay(50); //同时启动定时器,必须加delay _set_timer(2000000, REG_BASE_TIMER2);
2.3. 关闭定时器¶
_reset_timer(REG_BASE_TIMER0); _reset_timer(REG_BASE_TIMER1); _reset_timer(REG_BASE_TIMER2);
2.4. 中断处理¶
irqreturn_t interrupt_handle(int irq,void *dev_id) { printk("====> hit %d \n", irq); return IRQ_HANDLED; }
2.5. 完整demo¶
#include <linux/module.h> #include <linux/random.h> #include <linux/kthread.h> #include <linux/delay.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> //struct irq_domain #include <linux/of.h> //of_find_compatible_node #include <linux/irq.h> //IRQ_TYPE_LEVEL_HIGH #include <irqs.h> //INT_FIQ_TIMER_0 #define REG_BASE_TIMER0 (0xDE000000+0x1f000000+0x3020*2) #define REG_BASE_TIMER1 (0xDE000000+0x1f000000+0x3040*2) #define REG_BASE_TIMER2 (0xDE000000+0x1f000000+0x3060*2) #define ADDR_TIMER_CTL (0x0<<2) #define TIMER_ENABLE (0x1) /*设置循环触发*/ #define TIMER_TRIG (0x2) /*设置单次触发*/ #define TIMER_INTERRUPT (0x100) /*开启中断*/ #define ADDR_TIMER_HIT (0x1<<2) #define ADDR_TIMER_MAX_LOW (0x2<<2) #define ADDR_TIMER_MAX_HIGH (0x3<<2) #define ADDR_TIMER_DIV (0x6<<2) #define WRITE_WORD(_reg, _val) {(*((volatile unsigned short*)(_reg))) = (unsigned short)(_val); } static int vIrqNum0=0; static int vIrqNum1=0; static int vIrqNum2=0; irqreturn_t interrupt_handle(int irq,void *dev_id) { printk("====> hit %d \n", irq); return IRQ_HANDLED; } void _set_timer(unsigned int usec, unsigned int reg_base) { unsigned int interval; interval = usec*12; //set timer_clk to 12M for I2m/P3, (mcu_432m/12m)-1=0x23 WRITE_WORD(reg_base+ ADDR_TIMER_DIV, 0x23); WRITE_WORD(reg_base+ ADDR_TIMER_MAX_LOW, (interval &0xffff)); WRITE_WORD(reg_base+ ADDR_TIMER_MAX_HIGH, (interval >>16)); WRITE_WORD(reg_base, 0); WRITE_WORD(reg_base, TIMER_ENABLE|TIMER_INTERRUPT); /*设置循环触发*/ //WRITE_WORD(reg_base, TIMER_TRIG|TIMER_INTERRUPT); /*设置单次触发*/ } void _reset_timer(unsigned int reg_base) { WRITE_WORD(reg_base, 0); } static __init int mdrv_init(void) { printk(KERN_ALERT "Timer mdrv_init! \n"); /*timer0*/ { struct device_node *intr_node; struct irq_domain *intr_domain; struct irq_fwspec fwspec; intr_node = of_find_compatible_node(NULL, NULL, "sstar,main-intc"); intr_domain = irq_find_host(intr_node); if(!intr_domain) return -ENXIO; fwspec.param_count = 3; //refer to #interrupt-cells fwspec.param[0] = 0; //GIC_SPI fwspec.param[1] = INT_FIQ_TIMER_0; /* irqs.h */ fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; fwspec.fwnode = of_node_to_fwnode(intr_node); vIrqNum0 = irq_create_fwspec_mapping(&fwspec); if (request_irq(vIrqNum0, interrupt_handle, 0, "timer0", (void *)REG_BASE_TIMER0)) return -EBUSY; } #if 1 /*timer1*/ { struct device_node *intr_node; struct irq_domain *intr_domain; struct irq_fwspec fwspec; intr_node = of_find_compatible_node(NULL, NULL, "sstar,main-intc"); intr_domain = irq_find_host(intr_node); if(!intr_domain) return -ENXIO; fwspec.param_count = 3; //refer to #interrupt-cells fwspec.param[0] = 0; //GIC_SPI fwspec.param[1] = INT_FIQ_TIMER_1; /* irqs.h */ fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; fwspec.fwnode = of_node_to_fwnode(intr_node); vIrqNum1 = irq_create_fwspec_mapping(&fwspec); if (request_irq(vIrqNum1, interrupt_handle, 0, "timer1", (void *)REG_BASE_TIMER1)) return -EBUSY; } #endif #if 2 /*timer2*/ { struct device_node *intr_node; struct irq_domain *intr_domain; struct irq_fwspec fwspec; intr_node = of_find_compatible_node(NULL, NULL, "sstar,main-intc"); intr_domain = irq_find_host(intr_node); if(!intr_domain) return -ENXIO; fwspec.param_count = 3; //refer to #interrupt-cells fwspec.param[0] = 0; //GIC_SPI fwspec.param[1] = INT_FIQ_TIMER_2; /* irqs.h */ fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; fwspec.fwnode = of_node_to_fwnode(intr_node); vIrqNum2 = irq_create_fwspec_mapping(&fwspec); if (request_irq(vIrqNum2, interrupt_handle, 0, "timer2", (void *)REG_BASE_TIMER2)) return -EBUSY; } #endif //以下3个timer定时设为2秒,参数单位为us _set_timer(2000000, REG_BASE_TIMER0); mdelay(50); //同时启动定时器,必须加delay _set_timer(2000000, REG_BASE_TIMER1); mdelay(50); //同时启动定时器,必须加delay _set_timer(2000000, REG_BASE_TIMER2); return 0; } static __exit void mdrv_exit(void) { printk(KERN_ALERT " mdrv_exit!\n"); _reset_timer(REG_BASE_TIMER0); _reset_timer(REG_BASE_TIMER1); _reset_timer(REG_BASE_TIMER2); free_irq (vIrqNum0, (void *)REG_BASE_TIMER0); free_irq (vIrqNum1, (void *)REG_BASE_TIMER1); free_irq (vIrqNum2, (void *)REG_BASE_TIMER2); } module_init(mdrv_init); module_exit(mdrv_exit); MODULE_AUTHOR("SSTAR"); MODULE_DESCRIPTION("ms cpu load driver"); MODULE_LICENSE("GPL");
2.6. Makefile¶
KERNEL_PATH = /home/shaojun.ke/work/Pioneer3/kernel MODULE_NAME = $(shell basename ${PWD}) obj-m := $(MODULE_NAME).o $(MODULE_NAME)-y := mdrv_timer.o EXTRA_CFLAGS += -Idrivers/sstar/include EXTRA_CFLAGS += -Idrivers/sstar/include/pioneer3 all: make -C $(KERNEL_PATH) M=$(PWD) modules clean: make -C $(KERNEL_PATH) M=$(PWD) clean