这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 软件与操作系统 » RRH62000适配rtthread sensor v1版框架

共2条 1/1 1 跳转至

RRH62000适配rtthread sensor v1版框架

工程师
2025-02-23 00:14:06     打赏

       其实这个在之前已经发过,见https://forum.eepw.com.cn/thread/388069/1,但由于那一篇由于内容比较多,因此框架适配部分略过了,仅仅是贴出了适配后的完整代码,因此在此进一步的解析一下当时的适配过程。

适配过程

    首先,经过v1版sensor框架的整理,我们已经获得了v1版sensor框架适配的模板,如下:

#include <drivers/sensor.h>
 
struct sensor_device {
    // driver Param
};

static rt_size_t _polling_get_data(rt_sensor_t sensor, struct rt_sensor_data *data)
{
    struct sensor_device *dev = sensor->parent.user_data;

    if (sensor->info.type == RT_SENSOR_CLASS_NONE)
    {
        // TODO: Read and fill data to struct rt_sensor_data *data
        return 1;
    }
    
    return 0;
}

static rt_size_t _get_data(rt_sensor_t sensor, struct rt_sensor_data *data)
{
    struct sensor_device *dev = sensor->parent.user_data;

    if (sensor->info.type == RT_SENSOR_CLASS_NONE)
    {
        // TODO: Read and fill data to struct rt_sensor_data *data
        return 1;
    }
    
    return 0;
}

static rt_size_t fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    RT_ASSERT(buf);

    if (sensor->config.mode == RT_SENSOR_MODE_POLLING)
        return _polling_get_data(sensor, buf);
    else
        return _get_data(sensor, buf);
}

static rt_err_t control(struct rt_sensor_device *sensor, int cmd, void *args)
{
    rt_err_t result = RT_EOK;
    
    switch (cmd)
    {
    
        case RT_SENSOR_CTRL_GET_ID:
            // TODO:  get device id
            // result = xxxx(sensor, args);
            break;
        case RT_SENSOR_CTRL_SET_RANGE:
            // TODO: set test range
            // result = xxxx(sensor, args);
            break;
        case RT_SENSOR_CTRL_SET_ODR:
            // TODO: set frequency
            // result = xxxx(sensor, args);
            break;
        case RT_SENSOR_CTRL_SET_MODE:
            // TODO: set work mode
            // result = xxxx(sensor, args);
            break;
        case RT_SENSOR_CTRL_SET_POWER:
            // TODO: set power mode
            // result = xxxx(sensor, args);
            break;

        case RT_SENSOR_CTRL_SELF_TEST:
            // TODO: process self test
            // result = xxxx(sensor);
            break;
        default:
            return -RT_ERROR;
    }
    
    return result;
}

static struct rt_sensor_ops sensor_ops =
{
    fetch_data,
    control
};

int rt_hw_init(const char *name, struct rt_sensor_config *cfg)
{
    rt_int8_t result;
    rt_sensor_t sensor = RT_NULL; 
    struct sensor_device *dev;

    // TODO: dev init
    
    /* sensor register */
    sensor = rt_calloc(1, sizeof(struct rt_sensor_device));
    if (sensor == RT_NULL)
        goto __exit;
    
    sensor->info.type       = RT_SENSOR_CLASS_NONE; // Set real type
    sensor->info.vendor     = RT_SENSOR_VENDOR_UNKNOWN; // Set real vendor
    sensor->info.model      = "xxxx";  // set real model name
    sensor->info.unit       = RT_SENSOR_UNIT_NONE; // set to real unit flag
    sensor->info.intf_type  = RT_SENSOR_INTF_SPI; // Set interface type
    sensor->info.range_max  = SENSOR_RANGE_MAX; // Set to range max
    sensor->info.range_min  = SENSOR_RANGE_MIN; // Set to range min
    sensor->info.period_min = 50; // Set frequency

    rt_memcpy(&sensor->config, cfg, sizeof(struct rt_sensor_config));
    sensor->ops = &sensor_ops;

    result = rt_hw_sensor_register(sensor, name, RT_DEVICE_FLAG_RDONLY, dev);
    if (result != RT_EOK)
    {
        goto __exit;
    }

    return RT_EOK;

__exit:
    if (sensor)
        rt_free(sensor);
    // TODO: dev deinit
    return -RT_ERROR;
}

       在整理出这份文档后,后续的适配基本上就属于填空题了。

模块驱动代码编写

     在填空前,需要实现rrh62000的驱动代码,这部分没别的招,只能自己对着规格书一点一点的写(瑞萨做这方案的团队在美国,技术支持几乎没有,github上的驱动代码又和瑞萨的框架绑定过深,无法顺利剥离)。

v1传感器注册函数实现

     前期开发,需要聚焦,因此在适配时最好不要一次适配多个传感器功能,在这里,我优先适配了温度传感器的部分。

      首先,按照框架调用INIT_DEVICE_EXPORT(rrh62000_device_register)。

      其次,在这个函数中,先实现读取传感器关键信息,确保传感器受控。

      最后,按照框架所需信息,一个一个的修改传递给传感器注册函数rt_hw_sensor_register的信息,并调用注册接口实现传感器的注册。

static struct rt_sensor_ops sensor_ops =  
{  
    fetch_data,  
    control  
};  
  
struct sensor_device * rrh62000_param_init(void)  
{  
    struct sensor_device *dev;  
    struct rrh62000_firmwareversion fwVersion;  
    struct rrh62000_algoversion algoVersion;  
  
    dev = rt_calloc(1, sizeof(struct sensor_device));  
    if(!dev)  
    {  
        goto exit;  
    }  
    rt_memset(dev, 0x00, sizeof(struct sensor_device));  
    dev->dev = (struct rt_i2c_bus_device *)rt_device_find("i2c1");  
    if(dev->dev == RT_NULL)  
    {  
        goto exit;  
    }
  
    if(rrh62000_read_firmware_verison(dev->dev, &fwVersion) != RT_EOK)  
    {  
        goto exit;  
    }
  
    if(rrh62000_read_algoritm_verison(dev->dev, &algoVersion) != RT_EOK)  
    {  
        goto exit;  
    }
  
    rt_kprintf("rrh62000 firmware version %d.%d\n\r", fwVersion.major, fwVersion.minor);  
    rt_kprintf("rrh62000 algoritm version %d.%d.%d\n\r", algoVersion.major, algoVersion.minor, algoVersion.patch);  
  
    return dev;  
  
exit:  
    if(dev)
        rt_free(dev);  
    return RT_NULL;  
}  
  
int rt_hw_init(const char *name, struct rt_sensor_config *cfg)  
{  
    rt_int8_t result;  
    rt_sensor_t sensor = RT_NULL;   
    struct sensor_device *dev;  
  
    dev = rrh62000_param_init();  
    if(dev == RT_NULL)  
    {  
        goto __exit;  
    }  
      
    /* sensor register */  
    sensor = rt_calloc(1, sizeof(struct rt_sensor_device));  
    if (sensor == RT_NULL)  
        goto __exit;  
      
    sensor->info.type       = RT_SENSOR_CLASS_TEMP; // Set real type  
    sensor->info.vendor     = RT_SENSOR_VENDOR_UNKNOWN; // Set real vendor  
    sensor->info.model      = name;  // set real model name  
    sensor->info.unit       = RT_SENSOR_UNIT_DCELSIUS; // set to real unit flag  
    sensor->info.intf_type  = RT_SENSOR_INTF_I2C; // Set interface type  
    sensor->info.range_max  = 0xFFFF; // Set to range max  
    sensor->info.range_min  = 0x0000; // Set to range min  
    sensor->info.period_min = 50; // Set frequency  
  
    rt_memcpy(&sensor->config, cfg, sizeof(struct rt_sensor_config));  
    sensor->ops = &sensor_ops;  
  
    result = rt_hw_sensor_register(sensor, name, RT_DEVICE_FLAG_RDONLY, dev);  
    if (result != RT_EOK)  
    {  
        goto __exit;  
    }  
  
    return RT_EOK;  
  
__exit:  
    if (sensor)  
        rt_free(sensor);  
    if(dev)  
        rt_free(dev);  
    return -RT_ERROR;  
}  
  
static int rrh62000_device_register(void)  
{  
    struct rt_sensor_config      cfg = {  
        .mode = RT_SENSOR_MODE_POLLING,  
        .power = RT_SENSOR_POWER_DOWN,  
    };  
  
    rt_hw_init("rrh62000", &cfg);  
  
    return RT_EOK;  
  
}  
INIT_DEVICE_EXPORT(rrh62000_device_register);

读取数据接口实现

struct sensor_device {  
    rt_bool_t openCount;  
    struct rt_i2c_bus_device * dev;  
    struct rrh62000_data data;  
};  
  
static rt_ssize_t _get_data(rt_sensor_t sensor, struct rt_sensor_data *data)  
{  
    struct sensor_device *dev = sensor->parent.user_data;  
    rt_bool_t isDataReady = RT_FALSE;  
  
    if (sensor->info.type == RT_SENSOR_CLASS_TEMP)  
    {  
        if((rrh62000_read_data_status(dev->dev, &isDataReady) == RT_EOK) && (isDataReady))  
        {  
            rrh62000_read_measured_value(dev->dev, &dev->data);  
        }  
  
    }  
  
    if (sensor->info.type == RT_SENSOR_CLASS_TEMP)  
    {  
        data->data.temp = (dev->data.temperature.integer_part << 16) + dev->data.temperature.decimal_part;  
        data->timestamp = rt_sensor_get_ts();  
        return 1;  
    }  
      
    return 0;  
}  
  
static rt_ssize_t fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)  
{  
    RT_ASSERT(buf);  
  
    if (sensor->config.mode == RT_SENSOR_MODE_POLLING)  
        return _get_data(sensor, buf);  
    else  
        return 0;  
}

     注册函数实现后,后面就需要实现两个关键的函数了,一个时读取数据的接口fetch_data,一个是对传感器各项功能设置的control接口。读取数据的实现如下:

      需要注意的是,由于读数据只有一个接口,因此在实现时,需根据当前传感器的工作状态调用不同的读取实现,由于RRH62000只支持轮询读取的方式,因此读取实现就相对简单的多,仅仅需要读出数据并转换成struct rt_sensor_data格式的数据即可,对此结构体观察会发现,其实此结构体内部是个共用体,因此向上传递的数据需要按照温度传感器数据的格式上报,并记录读取数据的时间戳,若能读到传感器的时间使用传感器的时间,读不到,则使用rtt提供的时间。

控制接口实现

      控制接口需要实现的功能不少,有设置工作状态的命令set_power,有设置频率的接口set_odr,有设置两成的接口set_range,还有设置工作模式的接口set_mode。对于常见的传感器来说,这些接口都需要实现,但是呢,RRH62000除了传感器的开关,其他几个接口都只能使用默认参数去使用,因此适配起来就相对简单点了,仅仅需要适配set_power接口即可。

static rt_err_t _set_power(struct rt_sensor_device *sensor, void *args)  
{  
    struct sensor_device *dev = sensor->parent.user_data;  
    rt_uint8_t power = *(uint8_t *)args;  
    //fsp_err_t ret;  
    rt_err_t ret;  
    rt_bool_t isTVOCCLean = RT_FALSE;  
    int count = 70; // 70s  
  
    if(power == RT_SENSOR_POWER_DOWN)  
    {  
        if(dev->openCount != 0)  
        {  
            dev->openCount--;  
        }  
    }  
    else  
    {  
        if(dev->openCount == 0)  
        {  
            do  
            {  
                if(rrh62000_read_TVOC_sensor_clean_status(dev->dev, &isTVOCCLean) == RT_EOK)  
                {  
                    if(isTVOCCLean == RT_FALSE)  
                    {  
                        rt_thread_mdelay(1000);  
                        count--;  
                    }  
                }  
            }while((isTVOCCLean == RT_FALSE) && (count--));  
  
            if(count == 0)  
            {  
                goto RET;  
            }  
  
            ret = rrh62000_reset(dev->dev);  
            if(ret != RT_EOK)  
            {  
                rt_kprintf("open failed %d\n\r",ret);  
                goto RET;  
            }  

            rt_thread_mdelay(1000);  
  
            ret = rrh62000_moving_average_set(dev->dev, 1);  
            ret = rrh62000_fan_speed_set(dev->dev, 60);  
        }  
        dev->openCount++;  
    }  

    return RT_EOK;  

RET:  
    return -RT_ERROR;  
}  
  
static rt_err_t control(struct rt_sensor_device *sensor, int cmd, void *args)  
{  
    rt_err_t result = RT_EOK;  
      
    switch (cmd)  
    {  
        case RT_SENSOR_CTRL_GET_ID:  
            // TODO:  get device id  
            // result = xxxx(sensor, args);  
            break;  
        case RT_SENSOR_CTRL_GET_INFO:  
            // TODO:  get info  
            // result = xxxx(sensor, args);  
            break;  
        case RT_SENSOR_CTRL_SET_RANGE:  
            // TODO: set test range  
            // result = xxxx(sensor, args);  
            break;  
        case RT_SENSOR_CTRL_SET_ODR:  
            // TODO: set frequency  
            // result = xxxx(sensor, args);  
            break;  
        case RT_SENSOR_CTRL_SET_MODE:  
            // TODO: set work mode  
            // result = xxxx(sensor, args);  
            break;  
        case RT_SENSOR_CTRL_SET_POWER:  
            // TODO: set power mode  
            result = _set_power(sensor, args);  
            break;  
  
        case RT_SENSOR_CTRL_SELF_TEST:  
            // TODO: process self test  
            // result = xxxx(sensor);  
            break;  
        default:  
            return -RT_ERROR;  
    }  
      
    return result;  
}

测试接口编写

      对于格式接口,个人比较喜欢写成命令行命令的方式调用,这样可以通过传参的方式灵活验证验证接口的有效性,在这里也不例外,我写了如下代码:

#if 1  
#define RRH_TEMP_DEVICE_NAME "temp_rrh"  
void rrh6200_temp_read(void)  
{  
    rt_device_t dev = rt_device_find(RRH_TEMP_DEVICE_NAME);  
    rt_err_t result;  
    rt_uint32_t len;  
    struct rt_sensor_data data;  
  
    if(!dev)  
    {  
        rt_kprintf("No device name %s\n\r", RRH_TEMP_DEVICE_NAME);  
        return;  
    }  
    result = rt_device_open(dev,RT_DEVICE_FLAG_RDONLY);  
    if(result != RT_EOK)  
    {  
        rt_kprintf("Open %s Fail\n\r", RRH_TEMP_DEVICE_NAME);  
        return;  
  
    }  
      
    len = rt_device_read(dev, 0 ,&data,1);  
    if(len)  
    {  
        rt_kprintf("Temp %d.%d \n\r", data.data.temp >> 16, data.data.temp & 0x0000FFFF);  
    }  
  
    result = rt_device_close(dev);  
    if(result != RT_EOK)  
    {  
        rt_kprintf("Close %s Fail\n\r", RRH_TEMP_DEVICE_NAME);  
        return;  
  
    }  
  
}  
MSH_CMD_EXPORT(rrh6200_temp_read, rrh62000 temprate sample);  
#endif

      其中:MSH_CMD_EXPORT便是把此函数编译成命令行代码的实现入口。

总结

    编译验证部分,在之前发布的帖子中已经实现,在这里就不再说明。后续增加新的传感器类型,其实也是一样的套路,唯一的区别是,control和fetch_data中需要识别当前的传感器,根据传感器进行功能的实现。






关键词: RRH62000     适配     rtthread     sens    

专家
2025-02-23 21:20:54     打赏
2楼

感谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]