1Linux下SPI驱动框架
1.1 SPI驱动框架简介
SPI 驱动框架和 I2C 很类似,都分为主机控制器驱动和设备驱动,主机控制器也就是 SOC的 SPI 控制器接口。不管是什么 SPI 设备, SPI 控制器部分的驱动都是一样,我们的重点就落在了种类繁多的 SPI 设备驱动。
1.2 SPI主机驱动-spi_master
SPI 主机驱动就是 SOC 的 SPI 控制器驱动,类似 I2C 驱动里面的适配器驱动。 Linux 内核使用
spi_master
表示 SPI 主机驱动,spi_master
是个结构体:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 struct spi_master {
struct device dev;
struct list_head list;
s16 bus_num;
u16 num_chipselect;
u16 dma_alignment;
u16 mode_bits;
u32 bits_per_word_mask;
u32 min_speed_hz;
u32 max_speed_hz;
u16 flags;
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
bool bus_lock_flag;
int (*setup)(struct spi_device *spi);
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
.........
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
.........
};
transfer
函数,和i2c_algorithm
中的master_xfer
函数一样,控制器数据传输函数。
transfer_one_message
函数,也用于 SPI 数据发送,用于发送一个spi_message
, SPI 的数据会打包成spi_message
,然后以队列方式发送出去。也就是 SPI 主机端最终会通过 transfer 函数与 SPI 设备进行通信,因此对于 SPI 主机控制器的驱动编写者而言
transfer
函数是需要实现的,因为不同的 SOC 其 SPI 控制器不同,寄存器都不一样。和 I2C 适配器驱动一样, SPI 主机驱动一般都是 SOC 厂商去编写的,所以我们作为 SOC 的使用者,这一部分的驱动就不用操心了,除非你是在 SOC 原厂工作,内容就是写 SPI 主机驱动。
SPI 主机驱动的核心就是申请
spi_master
,然后初始化spi_master
,最后向 Linux 内核注册spi_master
。
spi_master
申请与释放
spi_alloc_master
函数用于申请spi_master
1
2 struct spi_master *spi_alloc_master(struct device *dev,
unsigned size)
dev:设备,一般是
platform_device
中的 dev 成员变量。size: 私有数据大小,可以通过
spi_master_get_devdata
函数获取到这些私有数据。返回值: 申请到的
spi_master
。
spi_master_put
函数用于释放spi_master
1 void spi_master_put(struct spi_master *master)
- master:要释放的
spi_master
。- 返回值: 无。
spi_master
的注册与注销
spi_master
注册函数为spi_register_master
1 int spi_register_master(struct spi_master *master)
- master:要注册的 spi_master。
- 返回值: 0,成功;负值,失败。
- I.MX6U 的 SPI 主机驱动会采用 spi_bitbang_start 这个 API 函数来完成 spi_master 的注册, spi_bitbang_start 函数内部其实也是通过调用 spi_register_master 函数来完成 spi_master 的注册。
spi_master
注销函数spi_unregister_master
1 void spi_unregister_master(struct spi_master *master)
- master:要注销的
spi_master
。- 返回值: 无。
- 如果使用
spi_bitbang_start
注册spi_master
的话就要使用spi_bitbang_stop
来注销掉spi_master
。
1.3 SPI设备驱动-spi_driver
Linux 内核使用
spi_driver
结构体来表示 spi 设备驱动,我们在编写 SPI 设备驱动的时候需要实 现spi_driver
。
1
2
3
4
5
6
7 struct spi_driver {
const struct spi_device_id *id_table;
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
struct device_driver driver;
};
spi_driver
注册函数为spi_register_driver
1 int spi_register_driver(struct spi_driver *sdrv)
- sdrv: 要注册的 spi_driver。
- 返回值:0,注册成功;赋值,注册失败。
spi_driver
的注销函数为spi_unregister_driver
1 void spi_unregister_driver(struct spi_driver *sdrv)
- sdrv: 要注销的 spi_driver。
- 返回值: 无
1.4 设备和驱动匹配过程
SPI 设备和驱动的匹配过程是由 SPI 总线来完成的, SPI总线为
spi_bus_type
1
2
3
4
5
6 struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
};从上,可以知道SPI 设备和驱动的匹配函数为
spi_match_device
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
of_driver_match_device
函数用于完成设备树设备和驱动匹配。比较 SPI 设备节点的compatible
属性和of_device_id
中的compatible
属性是否相等,如果相当的话就表示 SPI 设备和驱动匹配。
acpi_driver_match_device
函数用于 ACPI 形式的匹配。
spi_match_id
函数用于传统的、无设备树的 SPI 设备和驱动匹配过程。比较 SPI设备名字和spi_device_id
的name
字段是否相等,相等的话就说明 SPI 设备和驱动匹配。比较
spi_device
中 modalias 成员变量和device_driver
中的 name 成员变量是否相等。
1.5收发函数
当我们向 Linux 内核注册成功 spi_driver 以后就可以使用 SPI 核心层提供的 API 函数来对设备进行读写操作了。首先是 spi_transfer 结构体,此结构体用于描述 SPI 传输信息,结构体内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
* for MicroWire, one buffer must be null
* buffers must work with dma_*map_single() calls, unless
* spi_message.is_dma_mapped reports a pre-existing mapping
*/
const void *tx_buf;
void *rx_buf;
unsigned len;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned cs_change:1;
unsigned tx_nbits:3;
unsigned rx_nbits:3;
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};
- tx_buf 保存着要发送的数据。
- rx_buf 用于保存接收到的数据。
- len 是要进行传输的数据长度, SPI 是全双工通信,因此在一次通信中发送和接收的字节数都是一样的,所以
spi_transfer
中也就没有发送长度和接收长度之分。
spi_transfer
需要组织成spi_message
,spi_message
也是一个结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 struct spi_message {
struct list_head transfers;
struct spi_device *spi;
unsigned is_dma_mapped:1;
.......
/* completion is reported through a callback */
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
/* for optional use by whatever driver currently owns the
* spi_message ... between calls to spi_async and then later
* complete(), that's the spi_master controller driver.
*/
struct list_head queue;
void *state;
};在使用
spi_message
之前需要对其进行初始化,spi_message
初始化函数为spi_message_init
,函数原型如下:
1 void spi_message_init(struct spi_message *m)
m: 要初始化的
spi_message
。返回值: 无。
spi_message
初始化完成以后需要将spi_transfer
添加到spi_message
队列中,这里我们要用到spi_message_add_tail
函数,此函数原型如下:
1 void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
- t: 要添加到队列中的
spi_transfer
。- m:
spi_transfer
要加入的spi_message
。- 返回值: 无。
spi_message
准备好以后就可以进行数据传输了,数据传输分为同步传输和异步传输,同步传输会阻塞的等待 SPI 数据传输完成,同步传输函数为spi_sync
,函数原型如下:
1 int spi_sync(struct spi_device *spi, struct spi_message *message)
- spi: 要进行数据传输的
spi_device
。- message:要传输的
spi_message
。- 返回值: 无。
异步传输不会阻塞的等到 SPI 数据传输完成,异步传输需要设置
spi_message
中的 complete成员变量, complete 是一个回调函数,当 SPI 异步传输完成以后此函数就会被调用。 SPI 异步传输函数为spi_async
,函数原型如下:
1 int spi_async(struct spi_device *spi, struct spi_message *message)
- spi: 要进行数据传输的 spi_device。
- message:要传输的 spi_message。
- 返回值: 无。
综上所述,SPI 数据传输步骤如下:
- 申请并初始化
spi_transfer
,设置spi_transfer
的 tx_buf 成员变量, tx_buf 为要发送的数据。然后设置 rx_buf 成员变量, rx_buf 保存着接收到的数据。最后设置 len成员变量,也就是要进行数据通信的长度。- 使用
spi_message_init
函数初始化spi_message
。- 使用
spi_message_add_tail
函数将前面设置好的spi_transfer
添加到spi_message
队列中。- 使用
spi_sync
函数完成 SPI 数据同步传输。
2 NXP官方驱动
打开IMX自己写的SPI,文件名为spi-imx.c,找到
spi_imx_data
结构体、
1
2
3
4
5
6
7
8
9
10
11
12
13 struct spi_imx_data {
struct spi_bitbang bitbang;
struct completion xfer_done;
void __iomem *base;
struct clk *clk_per;
struct clk *clk_ipg;
unsigned long spi_clk;
.........
const struct spi_imx_devtype_data *devtype_data;
int chipselect[0];
};分析:主要部分
从probe函数开始,
注冊spi_master
初始化,最后一句吧master赋予了spi_max,
for循环是获取spi片选引脚,支持4个硬件片选
现在只需要对spi_imx初始化就行了
跳转到
spi_imx_setupxfer
函数,函数主要是这段,
1
2 spi_imx->rx = spi_imx_buf_rx_u8; //最终的SPI接收函数
spi_imx->tx = spi_imx_buf_tx_u8; //发送函数
1 spi_imx->devtype_data->config(spi_imx, &config);//对应的就是以下结构体的mx51_ecspi_config 配置6ULL的SPI控制寄存器
mx51_ecspi_config
这个函数就是配置SPI的bitbang下的spi_imx_transfer调用spi_imx_pio_transfer调用spi_imx_push调用spi_imx->tx(spi_imx);
接下来是中断处理接收
最终进入spi_bitbang_start
最终会使用spi_register_master向系统注册SPI。
3 驱动编写
3.1 修改设备树
设备树内添加以下
1
2
3
4
5
6
7
8 pinctrl_ecspio3: icm20602 {
fsl,pins = <
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1
>;
};
注意屏蔽掉其他占用的引脚,不光UART2_RX这些要屏蔽,GPIO1_IO20也要屏蔽。
这里片选信号不作为硬件片选,而是作为普通的GPIO,我们在程序里面自行控制片选引脚。
然后在ECSPI3节点下创建icm20602子节点。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 &ecspi3 {
fsl,spi-num-chipselects = <1>; //1个片选
cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>; //片选引脚,软件片选!
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
//对应的SPI芯片子节点
spidev0: icm20602@0 { //@后面的0表示SPI芯片接到哪个硬件片选上 6ull硬件接口支持4个硬件片选
reg = <0>;
compatible = "cbus,icm20602";
spi-max-frequency = <8000000>; //SPI时钟频率
};
};可以看到设备已经添加成功。
3.2 驱动程序
具体细节都在代码快内部有注释。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
//#include "math.h"
struct icm20602_dev {
void *private;
int cs_gpio;
struct device_node *nd;
signed int gyro_x_adc; /* 陀螺仪X轴原始值 */
signed int gyro_y_adc; /* 陀螺仪Y轴原始值 */
signed int gyro_z_adc; /* 陀螺仪Z轴原始值 */
signed int accel_x_adc; /* 加速度计X轴原始值 */
signed int accel_y_adc; /* 加速度计Y轴原始值 */
signed int accel_z_adc; /* 加速度计Z轴原始值 */
signed int temp_adc; /* 温度原始值 */
};
struct icm20602_dev icm20602dev;
//spi读寄存器
static int icm20602_read_regs(struct icm20602_dev *dev,u8 reg,void *buf,int len)
{
int ret = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device *)dev->private;
//片选拉低
gpio_set_value(dev->cs_gpio,0);
//构建spi_transfer
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL); //申请内存
//第一步:发送要读取的寄存器地址
txdata[0] = reg | 0x80; /* 写数据的时候首寄存器地址bit8要置1 */
t->tx_buf = txdata; /* 要发送的数据 */
t->len = 1;
spi_message_init(&m); /* 初始化spi_message */
spi_message_add_tail(t,&m); /* 将spi_transfer添加到spi_message队列 */
ret = spi_sync(spi,&m); /* 同步发送 */
//第二步:读取数据
txdata[0] = 0xff; //无效的 全双工
t->rx_buf = buf;
t->len = len;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
kfree(t);
//片选拉高
gpio_set_value(dev->cs_gpio,1);
return ret;
}
//spi写寄存器
static int icm20602_write_regs(struct icm20602_dev *dev,u8 reg,u8 *buf,int len)
{
int ret = 0;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device *)dev->private;
//片选拉低
gpio_set_value(dev->cs_gpio,0);
//构建spi_transfer
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
//第一步:发送要写的寄存器地址
txdata[0] = reg & ~0x80;
t->tx_buf = txdata;
t->len = 1;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
//第二步:读取数据
t->tx_buf = buf;
t->len = len;
spi_message_init(&m);
spi_message_add_tail(t,&m);
ret = spi_sync(spi,&m);
kfree(t);
//片选拉高
gpio_set_value(dev->cs_gpio,1);
return ret;
}
static void icm20602_read_regs(struct icm20602_dev *dev,u8 reg,void *buf,int len)
{
u8 data = 0;
struct spi_device *spi = (struct spi_device *)dev->private;
//片选拉低
gpio_set_value(dev->cs_gpio,0);
data = reg | 0x80; /* 写数据的时候首寄存器地址bit8要置1 */
spi_write(spi,&data,1); //发送要读取的寄存器地址
spi_read(spi,buf,len);
//片选拉高
gpio_set_value(dev->cs_gpio,1);
}
static void icm20602_write_regs(struct icm20602_dev *dev,u8 reg,u8 *buf,int len)
{
u8 data = 0;
struct spi_device *spi = (struct spi_device *)dev->private;
//片选拉低
gpio_set_value(dev->cs_gpio,0);
data = reg & ~0x80; /* 写数据的时候首寄存器地址bit8要置1 */
spi_write(spi,&data,1); //发送要读取的寄存器地址
spi_write(spi,buf,len); //发送要读取的寄存器地址
//片选拉高
gpio_set_value(dev->cs_gpio,1);
}
//读取单个寄存器
static unsigned char icm20602_read_reg(struct icm20602_dev *dev,u8 reg)
{
u8 data = 0;
icm20602_read_regs(dev,reg,&data,1);
return data;
}
//写入单个寄存器
static void icm20602_write_reg(struct icm20602_dev *dev,u8 reg,u8 data)
{
u8 buf = data;
icm20602_write_regs(dev,reg,&buf,1);
}
void icm20602_reginit(struct icm20602_dev *dev)
{
u8 val = 0;
icm20602_write_reg(dev,ICM20602_PWR_MGMT_1,0x80); //复位
mdelay(50);
val = icm20602_read_reg(dev,ICM20602_WHO_AM_I);
printk("icm20602 ID = %#X\r\n",val);
icm20602_write_reg(dev,ICM20602_PWR_MGMT_1,0x01); //自动选择时钟
icm20602_write_reg(dev,ICM20602_PWR_MGMT_2, 0x00); //开启陀螺仪和加速度计
icm20602_write_reg(dev,ICM20602_CONFIG, 0x01); //176HZ 1KHZ
icm20602_write_reg(dev,ICM20602_SMPLRT_DIV, 0x07); //采样速率 SAMPLE_RATE = INTERNAL_SAMPLE_RATE / (1 + SMPLRT_DIV)
icm20602_write_reg(dev,ICM20602_GYRO_CONFIG, 0x18); //±2000 dps
icm20602_write_reg(dev,ICM20602_ACCEL_CONFIG, 0x10); //±8g
icm20602_write_reg(dev,ICM20602_ACCEL_CONFIG_2, 0x03); //Average 8 samples 44.8HZ
icm20602_write_reg(dev,ICM20602_LP_MODE_CFG, 0x00); /* 关闭低功耗 */
icm20602_write_reg(dev,ICM20602_FIFO_EN, 0x00); /* 关闭FIFO */
}
static void icm20602_getdata(struct icm20602_dev *dev)
{
unsigned char data[14] = {0};
icm20602_read_regs(dev,ICM20602_ACCEL_XOUT_H,data,14);
dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]);
dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]);
dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]);
dev->temp_adc = (signed short)((data[6] << 8) | data[7]);
dev->gyro_x_adc = (signed short)((data[8] << 8) | data[9]);
dev->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);
dev->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}
static int icm20602_open(struct inode *inode, struct file *file)
{
file->private_data = &icm20602dev;
printk("icm20602_open!\r\n");
return 0;
}
static ssize_t icm20602_read(struct file *file, char __user *buf, size_t cnt, loff_t *offt)
{
signed int data[7];
long err = 0;
struct icm20602_dev *dev = file->private_data;
icm20602_getdata(dev);
data[0] = dev->gyro_x_adc;
data[1] = dev->gyro_y_adc;
data[2] = dev->gyro_z_adc;
data[3] = dev->accel_x_adc;
data[4] = dev->accel_y_adc;
data[5] = dev->accel_z_adc;
data[6] = dev->temp_adc;
err = copy_to_user(buf, data, sizeof(data));
return 0;
}
static ssize_t icm20602_write(struct file *file, const char __user *buf, size_t cnt, loff_t *offt)
{
//struct icm20602_dev *dev = file->private_data;
printk("icm20602_write!\r\n");
return 0;
}
static int icm20602_release(struct inode *inode, struct file *file)
{
printk("icm20602_release!\r\n");
return 0;
}
//字符设备操作集
struct file_operations icm20602_fops = {
.owner = THIS_MODULE,
.open = icm20602_open,
.read = icm20602_read,
.write = icm20602_write,
.release = icm20602_release,
};
struct miscdevice icm20602_miscdev = {
.minor = MISC_ICM20602_MINOR,
.name = MISC_ICM20602_NAME,
.fops = &icm20602_fops,
};
static int icm20602_probe(struct spi_device *spi)
{
int ret;
//搭建字符设备驱动框架 使用MISC注册驱动
ret = misc_register(&icm20602_miscdev);
if(ret < 0){
printk("icm20602 misc device register failed!\r\n");
ret = -EFAULT;
}
//获取片选引脚
icm20602dev.nd = of_get_parent(spi->dev.of_node);
icm20602dev.cs_gpio = of_get_named_gpio(icm20602dev.nd,"cs-gpio",0);
if (icm20602dev.cs_gpio < 0)
{
printk("can not get cs-gpio!\n");
goto fail_gpio;
}
ret = gpio_request(icm20602dev.cs_gpio,"cs");
if (ret < 0)
{
printk("cs-gpio request failed!\n");
}
ret = gpio_direction_output(icm20602dev.cs_gpio,1); //默认高电平
//初始化spi_device
spi->mode = SPI_MODE_0;
spi_setup(spi);
//设置私有数据
icm20602dev.private = spi;
printk("icm20602_misc_probe!\n");
//初始化ICM20602
icm20602_reginit(&icm20602dev);
return ret;
fail_gpio:
return 0;
}
static int icm20602_remove(struct spi_device *spi)
{
gpio_free(icm20602dev.cs_gpio);
misc_deregister(&icm20602_miscdev); //misc驱动卸载
printk("icm20602_spi_remove\n");
return 0;
}
//设备树匹配
static const struct of_device_id icm20602_of_match[] = {
{ .compatible = "cbus,icm20602"},
{ }
};
//传统匹配
static const struct spi_device_id icm20602_id[] = {
{ "cbus,icm20602", 0 },
{ }
};
struct spi_driver icm20602_driver = {
.driver = {
.name = "icm20602",
.owner = THIS_MODULE,
.of_match_table = icm20602_of_match,
},
.probe = icm20602_probe,
.remove = icm20602_remove,
.id_table = icm20602_id,
};
static int __init icm20602_init(void)
{
int ret = 0;
ret = spi_register_driver(&icm20602_driver);
printk("icm20602_init\n");
return ret;
}
static void __exit icm20602_exit(void)
{
spi_unregister_driver(&icm20602_driver);
}
module_init(icm20602_init);
module_exit(icm20602_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cbus");
3.3 应用程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
int main(int argc, char *argv[]){
int fd,ret;
char *filename;
signed short data[7];
signed int gyro_x_adc, gyro_y_adc, gyro_z_adc;
signed int accel_x_adc, accel_y_adc, accel_z_adc;
signed int temp_adc;
float gyro_x_act, gyro_y_act, gyro_z_act;
float accel_x_act, accel_y_act, accel_z_act;
float temp_act;
if (argc != 2){
printf("Error Usage!!!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if (fd < 0){
printf("file %s open failed!!!\r\n",filename);
return -1;
}
while (1)
{
ret = read(fd,&data,sizeof(data));
if (ret == 0)
{
gyro_x_adc = data[0];
gyro_y_adc = data[1];
gyro_z_adc = data[2];
accel_x_adc = data[3];
accel_y_adc = data[4];
accel_z_adc = data[5];
temp_adc = data[6];
/* 计算实际值 */
gyro_x_act = (float)(gyro_x_adc) / 16.4;
gyro_y_act = (float)(gyro_y_adc) / 16.4;
gyro_z_act = (float)(gyro_z_adc) / 16.4;
accel_x_act = (float)(accel_x_adc) / 2048;
accel_y_act = (float)(accel_y_adc) / 2048;
accel_z_act = (float)(accel_z_adc) / 2048;
temp_act = ((float)(temp_adc) - 25 ) / 326.8 + 25;
printf("\r\n原始值:\r\n");
printf("gx = %d, gy = %d, gz = %d\r\n", gyro_x_adc, gyro_y_adc, gyro_z_adc);
printf("ax = %d, ay = %d, az = %d\r\n", accel_x_adc, accel_y_adc, accel_z_adc);
printf("temp = %d\r\n", temp_adc);
printf("实际值:");
printf("act gx = %.2f°/S, act gy = %.2f°/S, act gz = %.2f°/S\r\n", gyro_x_act, gyro_y_act, gyro_z_act);
printf("act ax = %.2fg, act ay = %.2fg, act az = %.2fg\r\n", accel_x_act, accel_y_act, accel_z_act);
printf("act temp = %.2f°C\r\n", temp_act);
}
else
{
printf("read data failed!!!\r\n");
return -1;
}
usleep(200000); //200ms
}
close(fd);
return 0;
}
3.4 ICM20602寄存器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
3.5接线图
4 实验
输入以下命令:
1 ./icm20602App /dev/icm20602