【一】、中断底半部
1. 软中断 --->>> 执行在中断上下文 --->>> 会被中断打断,不会被软中断或进程打断 --->>> 可以完成耗时操作 2. tasklet --->>> 执行在中断上下文 --->>> 会被中断打断,不会被软中断或进程打断 --->>> 可以完成耗时操作 3. 工作队列 --->>> 执行在进程上下文 --->>> 会被中断、中断底半部、进程打断 --->>> 可以完成耗时操作,同时 也可以有进程调度相关的函数 中断底半部实现: [1]. 软中断 init/main.c --->>> start_kernel --->>> softirq_init(); /*********************************************************************** *功能:开启(初始化)软中断 *参数: * @nr 软中断枚举值 * @action 中断底半部处理函数 **********************************************************************/ void open_softirq(int nr, void (*action)(struct softirq_action *)) /************************************************ *功能:调度中断底半部 *参数: * @nr 软中断枚举值 ***********************************************/ void raise_softirq(unsigned int nr) [2]. tasklet 1. struct tasklet_struct 数据类型 2. 定义、初始化 /******************************************************************************* *功能:定义并初始化tasklet *参数: * @name tasklet结构体变量名 * @func tasklet底半部处理函数指针 * @data 私有数据 ******************************************************************************/ #define DECLARE_TASKLET(name, func, data) \ struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } /********************************************************************************************** *功能:初始化tasklet *参数: * @t tasklet结构体指针 * @func tasklet底半部处理函数指针 * @data 私有数据 *********************************************************************************************/ void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data); 3. 调度tasklet底半部 /******************************************************************** *功能:调度tasklet底半部 *参数: * @t tasklet结构体指针 *返回值:void *******************************************************************/ void tasklet_schedule(struct tasklet_struct *t) [3]. 工作队列 <linux/workqueue.h> 1. 工作队列结构体 struct work_struct 2. 定义、初始化 typedef void (*work_func_t)(struct work_struct *work); /********************************************************** *功能:定义并初始化workqueue *参数: * @n 工作队列变量名 * @f 工作队列底半部处理函数指针 *********************************************************/ #define DECLARE_WORK(n, f) \ struct work_struct n = __WORK_INITIALIZER(n, f) /********************************************************** *功能:初始化工作队列 *参数: * @_work 工作队列结构体指针 * @_func 工作队列底半部处理函数指针 *********************************************************/ INIT_WORK(struct work_struct * _work, _func) 3. 调度工作队列 /**************************************************** *功能:调度工作队列底半部 *参数: * @work 工作队列结构体指针 ***************************************************/ bool schedule_work(struct work_struct *work) 【二】、定时器 <linux/timer.h> jiffies :计数值 HZ expires = jiffies + HZ; //定时1s expires - jiffies + n*HZ; //定时ns struct timer_list { unsigned long expires; //定时器的定时时间 --->>> 计数值 void (*function)(unsigned long); //定时器中断处理函数指针 unsigned long data; //私有数据 }; /*************************************************************************** *功能:定义并初始化定时器 *参数: * @_name 定时器变量名 * @_function 定时器中断处理函数指针 * @_expires 定时时间计数值 * @_data 私有数据 **************************************************************************/ #define DEFINE_TIMER(_name, _function, _expires, _data) \ struct timer_list _name = \ TIMER_INITIALIZER(_function, _expires, _data) /********************************** *功能:初始化定时器 *参数: * @timer 定时器指针 *********************************/ init_timer(struct timer_list * timer) /* 开启定时器 */ void add_timer(struct timer_list *timer) /* 关闭定时器 */ int del_timer(struct timer_list *timer) /************************************************************************ *功能:修改定时器定时时间 *参数: * @timer struct timer_list * * @expires 定时时间值 ***********************************************************************/ int mod_timer(struct timer_list *timer, unsigned long expires)【三】、按键消抖 采用定时器延时消抖 延迟机制: 1. 定时器 2. 中断底半部 软中断 tasklet 工作队列 3. 内核延时函数 void ndelay(unsigned long x) mdelay(n) 4. 内核睡眠函数 void msleep(unsigned int msecs); unsigned long msleep_interruptible(unsigned int msecs); void ssleep(unsigned int seconds)【四】、IIC总线 platform IIC SPI dev platform_device i2c_client spi_device drv platform_driver i2c_driver spi_driver bus bus_type 电气特性: SDA:数据线 SCK:时钟线 同步 半双工 串行 时序(时序图): 起始信号:时钟线高电平期间,数据线产生负跳变 结束信号:时钟线高电平期间,数据线产生正跳变 协议: a). signal read: | start signal | Device address[6:0],W[7] | slave ACK | register address[7:0] | slave ACK | --->>> SR | Device address[6:0],R[7] | slave ACK | Data[7:0] | MACK | SP | b). signal write: | start signal | Device address[6:0],W[7] | slave ACK | register address[7:0] | slave ACK | --->>> Data[7:0] | slave ACK | SP | W: write = 0 R: read = 1 SR:repeated start condition SP:stop condition IIC驱动: 设备驱动层: 需要驱动工程师完成的,向应用层提供操作的接口(fops),向下层操作硬件 核心层: i2c-core.c 内核提供好的,提供设备驱动和总线驱动注册和注销的方法,还提供设备驱动和总线驱动的匹配方式 总线驱动层:i2c-s3c2410.c 内核提供好的,按照用户传递的数据和操作时序操作硬件 IIC设备驱动:<linux/i2c.h> struct i2c_driver { int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); struct device_driver driver; const struct i2c_device_id *id_table; //i2c idtable表 }; struct i2c_device_id { char name[I2C_NAME_SIZE]; //IIC设备名 kernel_ulong_t driver_data; //私有数据 }; /******************************************************** *功能:注册i2c驱动 *参数: * @driver struct i2c_driver * *******************************************************/ #define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver) void i2c_del_driver(struct i2c_driver *driver); i2c设备驱动注册过程: i2c_register_driver --->>> driver_register(&driver->driver); --->>> bus_add_driver(drv); --->>> driver_attach(drv); --->>> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); --->>> __driver_attach --->>> driver_match_device(drv, dev) --->>> drv->bus->match ? drv->bus->match(dev, drv) : 1 static int i2c_device_match(struct device *dev, struct device_driver *drv) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; if (!client) return 0; /* 设备数匹配 */ if (of_driver_match_device(dev, drv)) return 1; /* Then ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; driver = to_i2c_driver(drv); /* idtable表匹配 */ if (driver->id_table) return i2c_match_id(driver->id_table, client) != NULL; return 0; } 设备数匹配 > idtable表匹配 struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; /* I2C_M_RD 读数据 0 写数据 */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ }; /*********************************************************************************** *功能:i2c总线数据传输 *参数: * @adap struct i2c_adapter * * @msgs 消息结构体 * @num 消息结构体个数 **********************************************************************************/ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs,int num); 数据收发函数的封装: [1]. 发送数据 int write_reg(struct i2c_client *client,u8 reg,u8 val) { int ret = 0; u8 buf[] = {reg,val}; struct i2c_msg msgs[] = { [0] = { .addr = client->addr, .flags= 0, .len = 2, .buf = buf, }, }; ret = i2c_transfer(client->adapter,msgs,ARRAY_SIZE(msgs)); if(ret < 0){ return ret; } return 0; } [2]. 接受数据 int read_reg(struct i2c_client *client,u8 reg) { int ret = 0; u8 val = 0; u8 buf[] = {reg}; struct i2c_msg msgs[] = { [0] = { .addr = client->addr, .flags= 0, .len = 1, .buf = buf, }, [1] = { .addr = client->addr, .flags= I2C_M_RD, .len = 1, .buf = &val, }, }; ret = i2c_transfer(client->adapter,msgs,ARRAY_SIZE(msgs)); if(ret < 0){ return ret; } return (unsigned int)val; }