7.4、 LSM6DSL使用
文章目录
具体的使用个人觉得有三种办法,如下
-
根据datasheet写驱动
7.4.1、 LSM6DSL C_DRIVER库
ST针对自己家传感器推出的标准C驱动,在仓库内除去各个传感器驱动外,还有配套的例子.该库支持SPI和IIC接口.
7.4.1.1、LSM6DSL C_DRIVE移植
下载后打开lsm6dsl_STdC文件夹,目录如下.
参考lsm6dsl_read_data_polling.c
文件,想要使用的话,只需要添加两个函数即可.
软件IIC的话参考软件IIC这一步.
static int32_t platform_write(void *handle, uint8_t Reg,
uint8_t *Bufp,
uint16_t len)
{
if (handle == &hi2c1) {
HAL_I2C_Mem_Write(handle, LSM6DSL_I2C_ADD_H, Reg,
I2C_MEMADD_SIZE_8BIT, Bufp, len, 1000);
}
#ifdef SPI_TRANSMIT
else if (handle == &hspi2) {
HAL_GPIO_WritePin(CS_SPI2_GPIO_Port, CS_SPI2_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(handle, &Reg, 1, 1000);
HAL_SPI_Transmit(handle, Bufp, len, 1000);
HAL_GPIO_WritePin(CS_SPI2_GPIO_Port, CS_SPI2_Pin, GPIO_PIN_SET);
}
#endif
return 0;
}
static int32_t platform_read(void *handle, uint8_t Reg, uint8_t *Bufp,
uint16_t len)
{
if (handle == &hi2c1) {
HAL_I2C_Mem_Read(handle, LSM6DSL_I2C_ADD_H, Reg,
I2C_MEMADD_SIZE_8BIT, Bufp, len, 1000);
}
#ifdef SPI_TRANSMIT
else if (handle == &hspi2) {
Reg |= 0x80;
HAL_GPIO_WritePin(CS_DEV_GPIO_Port, CS_DEV_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(handle, &Reg, 1, 1000);
HAL_SPI_Receive(handle, Bufp, len, 1000);
HAL_GPIO_WritePin(CS_DEV_GPIO_Port, CS_DEV_Pin, GPIO_PIN_SET);
}
#endif
return 0;
}
然后在主函数内添加如下内容.
stmdev_ctx_t dev_ctx;
dev_ctx.write_reg = platform_write;
dev_ctx.read_reg = platform_read;
dev_ctx.handle = &hi2c1;
通过上述方法就可以具体调用lsm6dsl_reg.c文件内的api函数了,具体使用方法如下所示.
int main()
{
stmdev_ctx_t dev_ctx;
dev_ctx.write_reg = platform_write;
dev_ctx.read_reg = platform_read;
dev_ctx.handle = &hi2c1;
uint8_t whoamI = 0;
lsm6dsl_device_id_get(&dev_ctx, &whoamI);
if ( whoamI != LSM6DSL_ID )
while (1); /*manage here device not found */
}
7.4.2、LSM6DSL MEMS库
MEMS库大概就是对C_Driver库进行了一层封装,上述的C_Driver库只是对一些操作进行了基本的封装,大概类似于STM32的STD库或者HAL库,MEMS库则是在该封装之上在进行一层封装,且多出了16个运动算法库.
MEMS库架构如下所示.
- hardware : 你所使用的硬件平台
- hardware abstraction : 硬件平台本身的驱动及传感器的驱动
- middleware : MEMS库比C_DRIVER库多出的地方,该部分ST官方只提供了静态库文件,没有源码,这是一个独立于平台的软件层
- application : 应用逻辑层
emmmm,这套代码库东西很是挺多的,但我用不到这么多,下面简单说一下我的使用方法.
7.4.2.1、MEMS库使用方法
我使用的工具如下.
- 软件
- CUBEMX 6.1.0
- X-CUBE-MEMS 8.2.0
- STM32F4 Series 1.25.2
- 硬件
- STM32F411
- LSM6DSL
CUBEMX配置如下
- 选择使用的芯片及芯片硬件使用的接口
- 因为我是自己打的板子,所以我选择的是CUSTOM/MOTION_SENSER,如果官方支持的板子,选择BOARD Extension里面对应型号即可.
- 选择你想使用的例子. 四五步略
- 第六步,根据你硬件方面的接线去选择.
- 最后,BUS_IO_DRIVER目前只能选择硬件IIC,但是可以自己手动替换.
备注: 如果想要使用MEMS库里面的算法库,可以在第三步的下面找到你要使用的算法库勾选上,生成的工程里就会包含该静态库文件.关于这些库文件的API文档和使用方法,可以根据库不同参考ST官方的应用笔记.以Motion EC库举例,参考UM2225.里面有资源占用详情及API的说明等等.
关于生成后的工程目录如下所示.
红框内的内容是手动添加的调试组件和IIC驱动.其余为CUBEMX自动生成的代码.
7.4.2.2、MEMS库移植
和C_Driver库的移植大同小异.
- 将下图红框内文件复制并添加到自己的工程中并添加头文件路径.
简单说一下文件作用.
lsm6dsl_reg.c
就是LSM6DSL芯片的驱动,.lsm6dsl.c
只是对驱动文件做了一些简单的封装,custom_motion_sensor.c
和custom_motion_sensor_ex.c
则是将lsm6dsl.c
文件内容进行了分离,custom_motion_sensor.c
只包含一些最基本的数据读取功能,custom_motion_sensor_ex.c
则只包含了一些嵌入式功能.
app_mems_int_pin_a_interface.c
文件只是传感器中断引脚所接入控制器IO的硬件初始化及对应的中断事件处理.
- 这里我使用的是软件IIC,如果使用硬件IIC则直接进入下一步.
添加IIC驱动文件,我这里只是简单的测试,所以移植了原子的部分代码做了些简单的修改,具体内容如下.emmm,因为测试的时候求快,所以代码有点乱,使用的时候最好整理一下…
#include "myiic.h"
#include "delay.h"
int32_t IIC_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE();
//PH4,5初始化设置
GPIO_Initure.Pin = GPIO_PIN_2 | GPIO_PIN_10;
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull = GPIO_PULLUP; //上拉
GPIO_Initure.Speed = GPIO_SPEED_FAST; //快速
HAL_GPIO_Init(GPIOB, &GPIO_Initure);
IIC_SDA = 1;
IIC_SCL = 1;
return BSP_ERROR_NONE;
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA = 1;
IIC_SCL = 1;
delay_us(4);
IIC_SDA = 0; //START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL = 0; //钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL = 0;
IIC_SDA = 0; //STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL = 1;
IIC_SDA = 1; //发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime = 0;
IIC_SDA = 1;
delay_us(1);
IIC_SCL = 1;
delay_us(1);
SDA_IN(); //SDA设置为输入
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime > 250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL = 0; //时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL = 0;
SDA_OUT();
IIC_SDA = 0;
delay_us(2);
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL = 0;
SDA_OUT();
IIC_SDA = 1;
delay_us(2);
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(uint8_t txd)
{
uint8_t t;
SDA_OUT();
IIC_SCL = 0; //拉低时钟开始数据传输
for(t = 0; t < 8; t++)
{
if(txd & 0x80)
IIC_SDA = 1;
else
IIC_SDA = 0;
txd <<= 1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t IIC_Read_Byte(unsigned char ack)
{
unsigned char i, receive = 0;
SDA_IN();//SDA设置为输入
for(i = 0; i < 8; i++)
{
IIC_SCL = 0;
delay_us(2);
IIC_SCL = 1;
receive <<= 1;
if(READ_SDA)receive++;
delay_us(1);
}
if(!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
int32_t I2C_Read_Byte_Len(uint16_t _device_addr, uint16_t _reg, uint8_t *_pData, uint16_t _Len)
{
IIC_Start();
IIC_Send_Byte((_device_addr<<1) | 0);
if(IIC_Wait_Ack())
{
IIC_Stop();//产生一个停止条件
return BSP_ERROR_PERIPH_FAILURE;
}
IIC_Send_Byte(_reg & 0xFF); //发送低地址
if(IIC_Wait_Ack())
{
IIC_Stop();//产生一个停止条件
return BSP_ERROR_PERIPH_FAILURE;
}
IIC_Start();
IIC_Send_Byte((_device_addr<<1) | 1); //发器件地址
if(IIC_Wait_Ack())
{
IIC_Stop();//产生一个停止条件
return BSP_ERROR_PERIPH_FAILURE;
}
while(_Len)
{
if(_Len == 1)
*_pData = IIC_Read_Byte(0);//读数据,发送nACK
else
*_pData = IIC_Read_Byte(1);//读数据,发送ACK
_Len--;
_pData++;
}
IIC_Stop();//产生一个停止条件
return BSP_ERROR_NONE;
}
int32_t I2C_Write_Reg_Len(uint16_t _device_addr, uint16_t _reg, uint8_t *_pData, uint16_t _Len)
{
uint8_t i;
IIC_Start();
IIC_Send_Byte((_device_addr<<1) | 0);
if(IIC_Wait_Ack())
{
return BSP_ERROR_PERIPH_FAILURE;
}
IIC_Send_Byte(_reg & 0xFF); //发送低地址
IIC_Wait_Ack();
for(i = 0; i < _Len; i++)
{
IIC_Send_Byte(_pData[i]);//发送数据
if(IIC_Wait_Ack())
{
return BSP_ERROR_PERIPH_FAILURE;
}
}
IIC_Stop();//产生一个停止条件
return BSP_ERROR_NONE;
}
#ifndef _MYIIC_H
#define _MYIIC_H
#include "main.h"
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define SDA_IN() {GPIOB->MODER&=~(3<<(10*2));GPIOB->MODER|=0<<(10*2);} //PB10输入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(10*2));GPIOB->MODER|=1<<(10*2);} //PB10输出模式
//IO操作
#define IIC_SCL PBout(2) //SCL
#define IIC_SDA PBout(10) //SDA
#define READ_SDA PBin(10) //输入SDA
//IIC所有操作函数
int32_t IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void);//发送IIC开始信号
void IIC_Stop(void);//发送IIC停止信号
void IIC_Send_Byte(uint8_t txd);//IIC发送一个字节
uint8_t IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
uint8_t IIC_Wait_Ack(void);//IIC等待ACK信号
void IIC_Ack(void);//IIC发送ACK信号
void IIC_NAck(void);//IIC不发送ACK信号
int32_t I2C_Read_Byte_Len(uint16_t _device_addr, uint16_t _reg, uint8_t *_pData, uint16_t _Len) ;
int32_t I2C_Write_Reg_Len(uint16_t _device_addr, uint16_t _reg, uint8_t *_pData, uint16_t _Len);
#endif
主要修改内容在
I2C_Read_Byte_Len
和I2C_Write_Reg_Len
函数上面,这两个是按照DATASHEET上给出的时序图来做的.
修改lsm6dsl_reg.h文件内#define LSM6DSL_I2C_ADD_L 0xD5U
为#define LSM6DSL_I2C_ADD_L 0x6AU
,至于硬件IIC需不需要修改ID则需要具体测试一下.条件有限,只测试了软件IIC.
- 替换IIC读写函数.
打开custom_mems_conf.h
文件,修改如下宏.硬件IIC的话参考官方custom_bus.c文件.
#define CUSTOM_LSM6DSL_0_I2C_Init IIC_Init
#define CUSTOM_LSM6DSL_0_I2C_DeInit IIC_Init
#define CUSTOM_LSM6DSL_0_I2C_ReadReg I2C_Read_Byte_Len
#define CUSTOM_LSM6DSL_0_I2C_WriteReg I2C_Write_Reg_Len
- 初始化及搭建自己的应用逻辑.
根据自己硬件配置修改app_mems.c
文件中的MX_MEMS_Init
,根据自己业务逻辑修改MX_MEMS_Process
函数即可.
备注:不建议使用app_mems.c文件,但是在搭建自己应用逻辑时,建议参考
MX_MEMS_Init
函数.
测试时候使用的功能是LSM6DSL_FIFOContMode
,具体效果如下.
以上,关于LSM6DSL的使用结束了,下周边开发边整理LVGL的笔记.