Sensor应用指南¶
1. 介绍¶
Solution 的传感器管理系统完全基于 RT-Thread Sensor 架构 构建,复用其标准化的设备抽象与接口设计,降低跨硬件适配成本。开发者可参考 RT-Thread 官方文档深入学习核心框架(RT-Thread Sensor 架构官网介绍),本章节先对关键概念进行说明。
1.1 核心概念说明¶
概念 |
定义与作用 |
|---|---|
Sensor 设备抽象 |
RT-Thread 将所有传感器统一抽象为「设备对象」,通过 |
操作接口集 |
即 |
设备注册机制 |
通过 |
设备注册机制 |
RT-Thread 定义 |
1.2 代码位置¶
sensor原厂驱动:
\sdk\customer\peripherals\sensor传感器周期性调度:
solution\components\sensor\sensor_algo\timer传感器打开(sensor_reg_open):
solution\components\sensor\sensor_algo\reg算法调度框架以及驱动适配
accelerometer:
solution\components\sensor\sensor_algo\acc地磁:
solution\components\sensor\sensor_algo\mag光感:
solution\components\sensor\sensor_algo\asl心率:
solution\components\sensor\sensor_algo\hrgps:
solution\components\sensor\sensor_algo\gps
IPC service:
solution\components\sensor\sensor_service
2. Solution 适配的传感器型号¶
Solution 已完成以下几类主流传感器的驱动适配,开发者可直接调用或基于现有驱动扩展:
传感器类别 |
适配型号列表 |
核心应用场景 |
|---|---|---|
Gsensor(运动传感器) |
STK8321、STK8328C、LSM6DSL_STdC |
计步、姿态识别、运动状态检测 |
HR(心率传感器) |
VC32S、VC9202、HRS3300 |
实时心率监测、健康数据采集 |
MAG(地磁传感器) |
MMC56X3、QMC6310 |
方向定位、电子罗盘 |
光感传感器 |
LTR303 |
环境光强检测、屏幕亮度自适应 |
3. Solution 传感器管理思路¶
基于 RT-Thread 基础架构,Solution 设计了“注册-打开-调度-算法处理-数据上报”的全流程管理逻辑。
3.1 传感器注册(rt_hw_sensor_register)—— 器件与算法分离注册¶
为实现硬件与算法的解耦,Solution 采用“器件驱动”与“算法模块”分别注册的机制,二者通过设备名关联调用。
接口:
rt_hw_sensor_register(struct rt_sensor_device *sensor, const char *name, rt_uint32_t flag, void *data)接口定义在
sdk\rtos\rtthread\components\drivers\sensors\sensor.c。
参数说明
关键参数 |
说明 |
|---|---|
|
传感器设备结构体(需填充类型、操作接口、私有数据等) |
|
器件设备名(如“stk8321”,需唯一,用于设备查找与识别) |
|
设备标志(如 |
3.1.1 器件驱动注册¶
负责硬件控制(如 I2C 通讯、中断处理),注册为“器件设备”:
示例
// STK8321 加速度计器件注册 static struct rt_sensor_ops sensor_ops = { stk8321_fetch_data, stk8321_control }; static int rt_hw_stk8321_register(const char *name) { int ret; static char bus_name[] = STK8321_BUS_NAME; /* accelerometer sensor register */ { stk8321_acce = rt_calloc(1, sizeof(struct rt_sensor_device)); if (stk8321_acce == RT_NULL) return -1; stk8321_acce->info.type = RT_SENSOR_CLASS_ACCE; stk8321_acce->info.vendor = RT_SENSOR_VENDOR_STM; stk8321_acce->info.model = NULL; stk8321_acce->info.unit = RT_SENSOR_UNIT_MG; stk8321_acce->info.intf_type = RT_SENSOR_INTF_I2C; stk8321_acce->info.range_max = 256; stk8321_acce->info.range_min = 0; stk8321_acce->info.period_min = STK8321_PERIOD_TIMER; stk8321_acce->info.fifo_max = STK8321_BUFF_SIZE; stk8321_acce->data_buf = RT_NULL; stk8321_acce->data_len = 0; stk8321_acce->config.intf.dev_name = bus_name; stk8321_acce->config.irq_pin.pin = RT_PIN_NONE; stk8321_acce->ops = &sensor_ops; ret = rt_hw_sensor_register(stk8321_acce, name, RT_DEVICE_FLAG_RDWR, RT_NULL); if (ret != RT_EOK) { rt_kprintf("GS [%s] register err code: %d\n", name, ret); goto __exit; } } rt_kprintf("GS [%s] register success!\n", name); return RT_EOK; __exit: if (stk8321_acce) { rt_free(stk8321_acce); stk8321_acce = RT_NULL; } return -RT_ERROR; } /** * @brief gsensor register, register name need unique, and longth is less than RT_NAME_MAX */ int stk8321_register(void) { static char name[] = "stk8321"; int ret = rt_hw_stk8321_register(name); return ret; } INIT_COMPONENT_EXPORT(stk8321_register);
具体参见:
solution\components\sensor\sensor_algo\acc\stk8321\stk8321_gsensor_service.c
3.1.2 算法模块注册¶
负责原始数据处理(如滤波、特征提取),注册为“算法设备”:
示例逻辑:
// Gsensor 算法模块注册 /** * @brief gsensor ops for register; */ static struct rt_sensor_ops sensor_ops = { cywee_algo_fetch_data, cywee_algo_control }; /** * @brief gsensor algo register; * @param const char *name algo name; * @param struct rt_sensor_config *cfg sensor config include struct rt_sensor_intf intf; // sensor interface config struct rt_device_pin_mode irq_pin; // Interrupt pin, The purpose of this pin is to notification read data rt_uint8_t mode; //sensor work mode rt_uint8_t power; // sensor power mode rt_uint16_t odr; /// sensor out data rate rt_int32_t range; // sensor range of measurement */ static int rt_hw_cywee_algo_register(const char *name, struct rt_sensor_config *cfg) { /* magnetometer/compass sensor register */ cywee_algo = rt_calloc(1, sizeof(struct rt_sensor_device)); RT_ASSERT(cywee_algo) cywee_algo->info.type = RT_SENSOR_CLASS_ACCE; cywee_algo->info.vendor = RT_SENSOR_VENDOR_UNKNOWN; cywee_algo->info.model = "cywee_algo"; cywee_algo->info.unit = RT_SENSOR_UNIT_NONE; cywee_algo->info.intf_type = 0; cywee_algo->info.range_max = 220; cywee_algo->info.range_min = 1; cywee_algo->info.period_min = 1; cywee_algo->info.fifo_max = 1; cywee_algo->data_buf = RT_NULL; cywee_algo->data_len = 0; //must be 0, because don't use data buf rt_memcpy(&cywee_algo->config, cfg, sizeof(struct rt_sensor_config)); cywee_algo->ops = &sensor_ops; rt_err_t result = rt_hw_sensor_register(cywee_algo, name, RT_DEVICE_FLAG_RDWR, RT_NULL); if (result != RT_EOK) { rt_kprintf("device register err code: %d", result); goto __exit; } rt_kprintf("GS algo[%s] init success!\n", name); return RT_EOK; __exit: if (cywee_algo) { rt_free(cywee_algo); cywee_algo = RT_NULL; } return -RT_ERROR; } /** * @brief gsensor algo register, register name need unique, and longth is less than RT_NAME_MAX */ static int cyewee_algo_register(void) { //register cywee algo to gsensor algo. char name[RT_NAME_MAX] = "a_cywee"; //all pin will be configed in drv. struct rt_sensor_config cfg = {0}; cfg.irq_pin.pin = RT_PIN_NONE; //disable pin operation of sensor_close rt_hw_cywee_algo_register(name, &cfg); return 0; } INIT_COMPONENT_EXPORT(cyewee_algo_register);
3.2 传感器打开(sensor_reg_open)—— 建立器件与算法关联¶
sensor_reg_open 是 Solution 基于 RT-Thread 扩展的核心接口,负责在系统初始化阶段“连接”已注册的器件设备与算法设备,构建“硬件采集→算法处理”的数据链路,是传感器从“注册态”进入“可用态”的关键步骤。
3.2.1 核心功能与设计逻辑¶
该接口通过传感器类型作为匹配依据,自动完成器件与算法的绑定,无需开发者手动关联,核心逻辑分为4步:
按传感器类型(如加速度计、心率传感器)查找已注册的器件设备;
按“类型+固定后缀”的规则拼接算法设备名,查找已注册的算法设备;
将器件设备句柄存入算法设备的私有数据区,实现二者关联;
调用
rt_device_open打开器件与算法设备,使其进入可操作状态。
3.2.2 接口sensor_reg_open¶
rt_err_t sensor_reg_open(
const char *sensor_name, // 传感器器件名称
const char *algo_name, // 算法模块名称
sensor_reg_info_t *sensor_info, // 传感器注册信息结构体
rt_device_t *sensor_device // 输出:传感器设备句柄
);
参数说明
参数名 |
类型 |
描述 |
|---|---|---|
|
|
传感器器件名称(如 “stk8321”、“vc32s”),为 |
|
|
算法模块名称(如 “gsensor_algo”、“hr_algo”),为 |
|
|
传感器注册信息结构体,用于存储传感器类型、算法操作接口等关键数据,属于输入输出参数 |
|
|
输出参数,函数执行成功后返回打开的传感器设备句柄,后续可通过该句柄调用器件操作接口 |
返回值说明
返回值 |
含义说明 |
|---|---|
|
传感器器件打开、算法关联等所有操作均成功完成 |
|
传感器器件打开失败或算法关联失败 |
接口定义在:solution\components\sensor\sensor_algo\reg\sensor_reg.c
3.2.3 调用示例¶
在系统初始化函数中,按传感器类型批量调用 sensor_reg_open 即可激活设备,建立器件与算法的关联链路:
/**
* @brief gsensor init, including: open device ,algo, updata info ,
*/
static rt_err_t gsensor_init(void)
{
gsensor_device = RT_NULL;
sensor_info.type = SENSOR_GSENSOR;
char *gsensor_name = "acce_"G_SENSOR_NAME;
char *gs_algo_name = "acce_"G_ALGO_NAME;
if (RT_EOK != sensor_reg_open(gsensor_name, gs_algo_name, &sensor_info, &gsensor_device))
{
return RT_ERROR;
}
#if defined (USING_GSENSOR_DATA_STORE_LIST)
rt_list_init(&gsensor_data_store_list);
#endif
gsensor_id = sensor_info.sensor_id;
if (sensor_info.dev_period)
gsensor_period = sensor_info.dev_period;
if (sensor_info.fifo_len)
gsensor_fifo_len = sensor_info.fifo_len;
gsensor_acce_para = sensor_info.gs_acce_para;
return RT_EOK;
}
3.3 传感器周期性调度(sensor_timer)¶
Solution 基于 RT-Thread 软件定时器 实现传感器的自动化调度,替代原生轮询机制,可灵活配置采样周期,同时适配低功耗场景(支持动态调整周期)。该调度器是 “器件采集→算法处理→数据上报” 全流程的触发核心。
具体参见:solution\components\sensor\sensor_algo\timer\sensor_timer.c
3.3.1 流程¶
创建定时器:初始化周期性软定时器,绑定回调 sensor_timer_handle,加载所有sensor采集的最小周期。
周期触发回调:定时器按间隔触发回调,发送消息给 LCPU 线程。
执行算法调度:LCPU 线程接收消息,触发 sensor_timeout_cb,调用各传感器算法(如计步、心率计算)。
按传感器类型(加速度计、心率、地磁、光感等)的周期,调用相应的算法;
算法的输出结果分为event和data,通过 IPC 将处理后的数据上报至应用层。
3.3.2 52x 平台专属优化¶
为避免高优先级的任务干扰数据读取,额外增加:
初始化采集线程:创建与高优先级的 sensor_data_thread 及事件对象。
事件触发采集:定时器回调同时发送事件,线程收到信号后立即读取传感器原始数据,确保数据准确。
3.4 算法模块(sensor_algo)—— 按传感器类型实现数据处理¶
算法模块是 Solution 传感器系统的核心,负责将器件采集的原始数据(如 PPG 信号、加速度值)转换为业务可用数据(如心率、计步数)。
各类型传感器算法实现:
传感器类别 |
算法模块文件 |
适配器件型号 |
|---|---|---|
加速度计(Gsensor) |
gsensor_algo.c |
Solution适配cywee算法STK8321、STK8328C、LSM6DSL_STdC |
心率(HR) |
hr_algo.c |
VC32S、VC9202、HRS3300 |
地磁(MAG) |
mag_algo.c |
MMC56X3、QMC6310 |
光感 |
asl_algo.c |
LTR303 |
注意:Solution 适配了 cywee 算法,但集成的版本为 cywee 算法测试版,存在“数小时后算法自动重置”的限制。若需正式使用 cywee 算法,请联系 cywee 官方获取授权版本。
4. 如何新增一个传感器¶
在
sdk\customer\peripherals\sensor目录下创建以传感器名命名的目录,添加原生驱动。修改
sdk\customer\peripherals\sensor对应传感器类型的目录下kconfig文件,新建宏来实现驱动的控制。在
solution\components\sensor\sensor_algo目录下,根据传感器类型选择对应类型目录,新建该传感器名的子目录,参照已有目录完成驱动适配。若涉及新的 gsensor 算法,参照
cywee_lib\cwm_motion.c进行适配(通过rt_hw_sensor_register注册算法)。根据传感器算法调度周期,确认是否修改
sensor_timer.c中的SENSOR_TIMER_PERIOD及计时,并在sensor_timeout_cb或xxx_algo_scheduler中完成进一步适配。
4.1 添加心率驱动¶
以下以vc32s心率为例,实现快速添加驱动如下:
新建vcs32s目录,存放原厂的驱动,修改kconfig以及SConscript文件
驱动文件适配电源控制,参考原理图配置menuconfig的电源pin,并实现对应功能;
void vc32s_hw_power_onoff(bool onoff)
{
if (onoff)
{
#if defined(PMIC_CONTROL_SERVICE)
pmic_service_control(PMIC_CONTROL_HR, 1);
#endif
#if(VC32S_POW_PIN >= 0)
rt_pin_mode(VC32S_POW_PIN, PIN_MODE_OUTPUT);
rt_pin_write(VC32S_POW_PIN, PIN_HIGH);
#endif
#if(VC32S_VDDIO_POW_PIN>=0)
rt_pin_mode(VC32S_VDDIO_POW_PIN, PIN_MODE_OUTPUT);
rt_pin_write(VC32S_VDDIO_POW_PIN, PIN_HIGH);
#endif
LOG_I("hr_power_on\n");
}
else
{
#if defined(PMIC_CONTROL_SERVICE)
pmic_service_control(PMIC_CONTROL_HR, 0);
#endif
#if(VC32S_POW_PIN >= 0)
rt_pin_mode(VC32S_POW_PIN, PIN_MODE_OUTPUT);
rt_pin_write(VC32S_POW_PIN, PIN_LOW);
#endif
#if(VC32S_VDDIO_POW_PIN>=0)
rt_pin_mode(VC32S_VDDIO_POW_PIN, PIN_MODE_OUTPUT);
rt_pin_write(VC32S_VDDIO_POW_PIN, PIN_LOW);
#endif
LOG_I("hr_power_off\n");
}
}
VC32S要求使用中断,需要配置中断pin,以及线程。
static void vc32s_irq_init(void)
{
rt_pin_mode(VC32S_INT_BIT, PIN_MODE_INPUT);
rt_pin_irq_enable(VC32S_INT_BIT, 0);
#ifdef BSP_USING_PM // add v32s INT WAKE
GPIO_TypeDef *gpio = GET_GPIO_INSTANCE(VC32S_INT_BIT);
uint16_t gpio_pin = GET_GPIOx_PIN(VC32S_INT_BIT);
int8_t wakeup_pin = HAL_LPAON_QueryWakeupPin(gpio, gpio_pin);
RT_ASSERT(wakeup_pin >= 0);
pm_enable_pin_wakeup(wakeup_pin, AON_PIN_MODE_POS_EDGE);
#endif /* BSP_USING_PM */
}
static void vc32s_irq_deinit(void)
{
#ifdef BSP_USING_PM
GPIO_TypeDef *gpio = GET_GPIO_INSTANCE(VC32S_INT_BIT);
int8_t gpio_pin = GET_GPIOx_PIN(VC32S_INT_BIT);
int8_t wakeup_pin = HAL_LPAON_QueryWakeupPin(gpio, gpio_pin);
RT_ASSERT(wakeup_pin >= 0);
pm_disable_pin_wakeup(wakeup_pin);
#endif /* BSP_USING_PM */
rt_pin_irq_enable(VC32S_INT_BIT, 0);
rt_pin_detach_irq(VC32S_INT_BIT);
}
中断pin除了配置为中断外,还需要配置为唤醒源,
#ifdef BSP_USING_PM // add v32s INT WAKE
GPIO_TypeDef *gpio = GET_GPIO_INSTANCE(VC32S_INT_BIT);
uint16_t gpio_pin = GET_GPIOx_PIN(VC32S_INT_BIT);
int8_t wakeup_pin = HAL_LPAON_QueryWakeupPin(gpio, gpio_pin);
RT_ASSERT(wakeup_pin >= 0);
pm_enable_pin_wakeup(wakeup_pin, AON_PIN_MODE_POS_EDGE);
#endif /* BSP_USING_PM */
通讯接口配置,vc32s使用的I2C2,需要配置menuconfig使能I2C2 ,并且配置I2C 初始化,id检查, i2c_write, i2c_read等。
I2C初始化参考
rt_err_t vc32s_hw_init(void)
{
rt_uint16_t flag_open = RT_DEVICE_FLAG_RDWR
#ifdef VC32S_USE_DMA
| RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX
#endif
;
vc32s_i2cbus = (struct rt_i2c_bus_device *)rt_device_find(VC32S_I2C_BUS);
if (RT_Device_Class_I2CBUS != vc32s_i2cbus->parent.type)
{
vc32s_i2cbus = NULL;
}
if (vc32s_i2cbus)
{
rt_device_open((rt_device_t)vc32s_i2cbus, flag_open);
#ifdef VC32S_USE_DMA
LOG_I("HR vc32s i2c use DMA ! \n");
#endif
LOG_I("find bus %s;\n", VC32S_I2C_BUS);
}
else
{
LOG_I("bus not find\n");
return RT_ERROR;
}
{
struct rt_i2c_configuration configuration =
{
.mode = 0,
.addr = 0,
.timeout = 200,
.max_hz = 400000,
};
rt_i2c_configure(vc32s_i2cbus, &configuration);
}
if (RT_EOK != vc32s_init())
{
vc32s_hw_deinit();
return RT_ERROR;
}
return RT_EOK;
}
id检查
static rt_err_t vc32s_init(void)
{
uint8_t chip_id = 0;
vc32sReadRegisters_ex(0x00, &chip_id, 1);
LOG_I("hr_vc32s_init chip_id =0x%x(should be 0x21)\n", chip_id);
if (chip_id == VC32S_ID)
{
vc32s_id = chip_id;
return RT_EOK;
}
return RT_ERROR;
}
i2c write
static rt_err_t vc32_i2c_base_write(rt_uint8_t *buf, rt_uint16_t len)
{
rt_int8_t res = 0;
struct rt_i2c_msg msgs;
msgs.addr = VC32S_SLAVE_ADDRESS; /* slave address */
msgs.flags = RT_I2C_WR; /* write flag */
msgs.buf = buf; /* Send data pointer */
msgs.len = len;
if (rt_i2c_transfer(vc32s_i2cbus, &msgs, 1) == 1)
{
res = RT_EOK;
}
else
{
res = -RT_ERROR;
}
return res;
}
i2c read
rt_err_t vc32s_i2c_read(unsigned char regAddr,
unsigned char len,
unsigned char *data)
{
rt_int8_t res = 0;
rt_uint8_t buf[2];
buf[0] = regAddr; //cmd
struct rt_i2c_msg msgs[2];
msgs[0].addr = VC32S_SLAVE_ADDRESS; /* Slave address */
msgs[0].flags = RT_I2C_WR; /* Read flag */
msgs[0].buf = buf; /* Read data pointer */
msgs[0].len = 1; /* Number of bytes read */
msgs[1].addr = VC32S_SLAVE_ADDRESS; /* Slave address */
msgs[1].flags = RT_I2C_RD; /* Read flag */
msgs[1].buf = data; /* Read data pointer */
msgs[1].len = len; /* Number of bytes read */
//LOG_I("vc32s_i2c_read \n");
if (rt_i2c_transfer(vc32s_i2cbus, msgs, 2) == 2)
{
res = RT_EOK;
}
else
{
res = -RT_ERROR;
}
return res;
}
校准tick 配置, vc32s 的时钟需要通过系统时钟进行校准;
static uint32_t vcHr02GetRtcCountFromMCU(void)
{
return HAL_GTIMER_READ(); //get tick
}
static void vcHr02Init(vcHr02_t *pVcHr02)
{
mcuOscData = HAL_LPTIM_GetFreq(); //lptime freq
}
在目录
solution\components\sensor\sensor_algo\hr添加对应的器件目录“vc32s”,在“vc32s"目录下创建vc32s_hr_service.c/vc32s_hr_service.h文件,用于实现vc32s的注册为rtos设备;
static int rt_hw_vc32s_register(const char *name, struct rt_sensor_config *cfg)
{
/* magnetometer/compass sensor register */
{
vc32s_sensor = rt_calloc(1, sizeof(struct rt_sensor_device));
if (vc32s_sensor == RT_NULL)
goto __exit;
vc32s_sensor->info.type = RT_SENSOR_CLASS_HR;
vc32s_sensor->info.vendor = RT_SENSOR_VENDOR_UNKNOWN;
vc32s_sensor->info.model = name;
vc32s_sensor->info.unit = RT_SENSOR_UNIT_BPM;
vc32s_sensor->info.intf_type = RT_SENSOR_INTF_I2C;
vc32s_sensor->info.range_max = 220;
vc32s_sensor->info.range_min = 30;
vc32s_sensor->info.period_min = VC32S_PERIOD_TIMER;
vc32s_sensor->info.fifo_max = VC32S_BUFF_SIZE;
vc32s_sensor->data_buf = RT_NULL;
vc32s_sensor->data_len = 0; //must be 0, because don't use data buf
rt_memcpy(&vc32s_sensor->config, cfg, sizeof(struct rt_sensor_config));
vc32s_sensor->ops = &sensor_ops;
rt_err_t result = rt_hw_sensor_register(vc32s_sensor, name, RT_DEVICE_FLAG_RDWR, RT_NULL);
if (result != RT_EOK)
{
VC32S_LOG_I("HR [%s] register err code: %d", name, result);
goto __exit;
}
}
VC32S_LOG_I("HR [%s] init success!\n", name);
return RT_EOK;
__exit:
if (vc32s_sensor != RT_NULL)
{
rt_free(vc32s_sensor);
vc32s_sensor = RT_NULL;
}
return -RT_ERROR;
}
int vc32s_register(void)
{
//all pin will be configed in drv.
struct rt_sensor_config cfg = {0};
cfg.irq_pin.pin = RT_PIN_NONE; //disable pin operation of sensor_close
int ret = rt_hw_vc32s_register("vc32s", &cfg);
rt_err_t err = rt_mutex_init(&vc32s_service_mutex, "vc32s_service_mutex", RT_IPC_FLAG_FIFO);
return ret;
}
INIT_COMPONENT_EXPORT(vc32s_register);
注册设备名长度不建议超过系统要求。可以通过宏RT_NAME_MAX来查看当前系统支持的最长名字;注册为RTOS设备,需要完善RTOS设备的接口,必须要实现的内容如下:
static struct rt_sensor_ops sensor_ops =
{
vc32s_fetch_data,
vc32s_control
};
vc32s_fetch_data 用于服务层从驱动获取数据;
vc32s_control 用于驱动各种控制功能,必须要实现的部分如下:
服务层调度文件hr_algo.c文件,修改使用的心率器件为“vc32s”,这个名字和注册的设备名要对应;
至此,心率器件的驱动能够正常运行了。
4.2 添加gsensor驱动¶
以下以stk8321 加速度计为例,实现快速添加加速度计驱动如下:
新建stk8321目录,存放原厂的驱动,修改kconfig以及SConscript文件
加速度计驱动需要适配内容包括:通讯总线,延时,中断及中断处理线程等。下面先适配通讯总线;
总线初始化
static int stk8321_i2c_init(void)
{
/* get i2c bus device */
gs_content.bus_handle = (struct rt_i2c_bus_device *)rt_device_find(GS_BUS_NAME);
if (RT_Device_Class_I2CBUS != gs_content.bus_handle->parent.type)
{
gs_content.bus_handle = NULL;
}
if (gs_content.bus_handle)
{
rt_device_open((rt_device_t)gs_content.bus_handle, RT_DEVICE_FLAG_RDWR);
}
else
{
rt_kprintf("gs bus not find ! \n");
return -RT_ERROR;
}
{
struct rt_i2c_configuration configuration =
{
.mode = 0,
.addr = 0,
.timeout = 200,
.max_hz = 400000,
};
rt_i2c_configure(gs_content.bus_handle, &configuration);
}
rt_kprintf("gs i2c bus init ok ! \n");
return 0;
}
总线读
static int32_t gs_i2c_read(GS_CONT_T *ctx, uint8_t reg, uint8_t *data, uint16_t len)
{
rt_size_t res = 0;
GS_CONT_T *handle = ctx;
if (handle && handle->bus_handle && data)
{
struct rt_i2c_msg msgs[2];
msgs[0].addr = GS_I2C_ADDR; /* slave address */
msgs[0].flags = RT_I2C_WR; /* write flag */
msgs[0].buf = ® /* Send data pointer */
msgs[0].len = 1;
msgs[1].addr = GS_I2C_ADDR; /* slave address */
msgs[1].flags = RT_I2C_RD; /* write flag */
msgs[1].buf = data; /* Send data pointer */
msgs[1].len = len;
if (rt_i2c_transfer(handle->bus_handle, msgs, 2) == 2)
{
res = RT_EOK;
}
else
{
res = -RT_ERROR;
}
}
return res;
}
总线写
static int32_t stk8321_i2c_write(GS_CONT_T *ctx, uint8_t reg, uint8_t *data, uint16_t len)
{
rt_int8_t res = 0;
uint8_t value[2];
GS_CONT_T *handle = ctx;
if (handle && handle->bus_handle && data)
{
struct rt_i2c_msg msgs;
value[0] = reg;
value[1] = *data;
msgs.addr = GS_I2C_ADDR; /* slave address */
msgs.flags = RT_I2C_WR; /* write flag */
msgs.buf = &value[0]; /* Send data pointer */
msgs.len = sizeof(value);
if (rt_i2c_transfer(handle->bus_handle, &msgs, 1) == 1)
{
res = RT_EOK;
}
else
{
struct rt_i2c_configuration configuration =
{
.mode = 0,
.addr = 0,
.timeout = 5000,
.max_hz = 400000,
};
rt_i2c_configure(gs_content.bus_handle, &configuration);
res = -RT_ERROR;
}
}
return res;
}
延时
void delay_ms(uint16_t ms)
{
rt_thread_mdelay(ms);
}
中断 线程
static void stk8321_int_handle(void *args)
{
rt_kprintf("stk8321_int_handle\n");
rt_pin_detach_irq(GS_INT);
rt_sem_release(&gs_int_sem);
}
static int stk8321_gpio_int_enable(void)
{
struct rt_device_pin_mode m;
rt_pin_mode(GS_INT, PIN_MODE_INPUT);
rt_pin_attach_irq(GS_INT, PIN_IRQ_MODE_RISING, stk8321_int_handle, (void *)(rt_uint32_t)GS_INT);
rt_pin_irq_enable(GS_INT, 1);
return 0;
}
static int stk8321_gpio_int_disable(void)
{
rt_pin_irq_enable(GS_INT, 0);
rt_pin_detach_irq(GS_INT);
return 0;
}
static void stk8321_thread_task(void *params)
{
int32_t ret;
uint8_t status = 0;
while (1)
{
rt_sem_take(&gs_int_sem, RT_WAITING_FOREVER);
rt_pin_irq_enable(GS_INT, 0);
//add fetchdata
rt_pin_attach_irq(GS_INT, PIN_IRQ_MODE_RISING, stk8321_int_handle, (void *)(rt_uint32_t)GS_INT);
rt_pin_irq_enable(GS_INT, 1);
}
}
static int stk8321_thread_init(void)
{
rt_sem_init(&gs_int_sem, "gs_sem", 0, RT_IPC_FLAG_FIFO);
gs_thread = rt_thread_create("gs_thread_init", stk8321_thread_task, NULL, GS_THRED_STACK_SIZE, 15, 10); //HR_THRED_STACK_SIZE
RT_ASSERT(gs_thread);
return 0;
}
static void stk8321_thread_deinit(void)
{
if (gs_thread)
{
rt_thread_delete(gs_thread);
gs_thread = NULL;
}
rt_sem_detach(&gs_int_sem);
rt_kprintf("gs_thread_deinit\n");
}
在目录“siflisolution\solution\components\sensor\sensor_algo\acc”添加对应的器件目录“stk8321”,在“stk8321"目录下创建stk8321_gsensor_service.c/stk8321_gsensor_service.h文件,用于实现stk8321的注册为rtos设备;
static int rt_hw_stk8321_register(const char *name, struct rt_sensor_config *cfg)
{
int ret;
/* accelerometer sensor register */
{
stk8321_acce = rt_calloc(1, sizeof(struct rt_sensor_device));
if (stk8321_acce == RT_NULL)
return -1;
stk8321_acce->info.type = RT_SENSOR_CLASS_ACCE;
stk8321_acce->info.vendor = RT_SENSOR_VENDOR_STM;
stk8321_acce->info.model = name;
stk8321_acce->info.unit = RT_SENSOR_UNIT_MG;
stk8321_acce->info.intf_type = RT_SENSOR_INTF_I2C;
stk8321_acce->info.range_max = 256;
stk8321_acce->info.range_min = 0;
stk8321_acce->info.period_min = 200;
stk8321_acce->info.fifo_max = 10;
stk8321_acce->data_buf = RT_NULL;
stk8321_acce->data_len = 0;
rt_memcpy(&stk8321_acce->config, cfg, sizeof(struct rt_sensor_config));
stk8321_acce->ops = &sensor_ops;
ret = rt_hw_sensor_register(stk8321_acce, name, RT_DEVICE_FLAG_RDWR, RT_NULL);
if (ret != RT_EOK)
{
rt_kprintf("GS [%s] register err code: %d\n", name, ret);
goto __exit;
}
}
rt_kprintf("GS [%s] register success!\n", name);
return RT_EOK;
__exit:
if (stk8321_acce)
{
rt_free(stk8321_acce);
stk8321_acce = RT_NULL;
}
return -RT_ERROR;
}
/**
* @brief gsensor register, register name need unique, and longth is less than RT_NAME_MAX
* /
int stk8321_register(void)
{
struct rt_sensor_config cfg = {0};
cfg.intf.dev_name = STK8321_BUS_NAME;
cfg.irq_pin.pin = STK8321_INT; //it must be config
int ret = rt_hw_stk8321_register("stk8321", &cfg);
return ret;
}
INIT_COMPONENT_EXPORT(stk8321_register);
注册设备名长度不建议超过系统要求。可以通过宏RT_NAME_MAX来查看当前系统支持的最长名字;
注册为RTOS设备,需要完善RTOS设备的接口,必须要实现的内容如下:
static struct rt_sensor_ops sensor_ops =
{
stk8321_fetch_data,
stk8321_control
};
stk8321_fetch_data 用于服务层向驱动层获取数据; stk8321_control 用于服务层对驱动层的控制; 必须实现的内容如下:
服务层调度文件gensor_algo.c文件,修改使用的gsensor器件为“stk8321”,这个名字和注册的设备名要对应;
至此,加速度计的驱动能够正常运行了。