【一】、中断底半部
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;
}