前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RT-Thread Nano如何适配ADC设备API

RT-Thread Nano如何适配ADC设备API

作者头像
Rice加饭
发布2022-05-10 18:18:37
6320
发布2022-05-10 18:18:37
举报
文章被收录于专栏:Rice嵌入式

本文介绍了如何在 RT-Thread Studio 上使用 RT-Thread Nano,并基于 BearPI-IOT STM32L431RCT6 的基础工程进行讲解如何使用 ADC 设备接口。

BearPI-IOT board

为什么需要设备接口

  1. RT-Thread 分为标准版本和 Nano 版本,其特点如下:
    • RT-Thread 标准版:拥有驱动框架,软件包等组件,软件包都是基于设备驱动接口来实现。
    • RT-Thread Nano :仅仅只是一个 RTOS 内核。没有任何组件。
  2. Nano 是无法直接使用 RT-Thread 丰富软件包功能。
  3. Nano 是一个面向低资源的 MCU 等芯片,不可能增加如同标准版的设备驱动框架。
  4. Nano 需要一套统一设备驱动 API,屏蔽不同芯片的 HAL 层的区别。方便移植工程到不同的平台。
  5. Nano 需要一套设备驱动 API,可以方便使用丰富软件包组件。

准备工作

  1. 使用 RT-Thread Studio 建立一个 STM32L431RCT6 的 RT-Thread Nano 基础工程。
  2. 基础工程创建可参考:在 RT-Thread Studio 上使用 RT-Thread Nano

ADC 设备接口

  1. 在 RT-Thread 标准版中,ADC设备驱动提供了一套设备管理接口来访问 ADC,用户程序可以直接使用该 API 操作 ADC 的功能,设备管理接口如下:

「函数」

「描述」

rt_device_find()

根据 ADC 设备名称查找设备获取设备句柄

rt_adc_enable()

使能 ADC 设备

rt_adc_read()

读取 ADC 设备数据

rt_adc_disable()

关闭 ADC 设备

  1. 由于 RT-Thread Nano 不使用设备驱动框架,所以没有对应的 rt_device_find() 这个API获取设备对象。为了能够与 RT-Thread 标准版的接口相近,我们需要做了简单的修改,设备管理接口如下:

「函数」

「描述」

rt_adc_device_find()

根据 ADC 设备名称查找设备获取设备句柄

rt_adc_enable()

使能 ADC 设备

rt_adc_read()

读取 ADC 设备数据

rt_adc_disable()

关闭 ADC 设备

  1. 对于 RT-Thread Nano,只需要适配如上这套 API,便可简单修改后使用 RT-Thread 丰富软件包功能。

适配 ADC 设备驱动接口

  1. 复制 RT-Thread 完整版工程中的 adc.h 文件(路径:rt-thread\components\drivers\include\drivers\adc.h)到我们准备好的 STM32L431RCT6 的 RT-Thread Nano 基础工程中。
  2. 由于 RT-Thread Nano 没有驱动框架,所以我们要把 adc.h 中有关完整版的内容去掉。整理完之后的 adc.h 文件如下:
代码语言:javascript
复制
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-08-22     RiceChen      first version
 */

#ifndef __ADC_H__
#define __ADC_H__
#include <rtthread.h>

struct rt_adc_device
{
    uint8_t *user_data;
};
typedef struct rt_adc_device *rt_adc_device_t;

typedef enum
{
    RT_ADC_CMD_ENABLE,
    RT_ADC_CMD_DISABLE,
} rt_adc_cmd_t;

struct rt_adc_device *rt_adc_device_find(const char *name);
rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_uint32_t channel);
rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_uint32_t channel);
rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_uint32_t channel);

#endif /* __ADC_H__ */
  1. 我们需要适配如上4个 ADC 设备 API,参考实例:drv_adc.c 和 drv_adc.h。
  • drv_adc.c实例:
代码语言:javascript
复制
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author            Notes
 * 2021-08-21     RiceChen     the first version
 */

#include <board.h>
#include "drv_adc.h"

#ifdef RT_USING_ADC

struct rt_i2c_config
{
    char *name;
    ADC_HandleTypeDef ADC_Handler;
};

struct rt_i2c_config adc_config[] =
{
#ifdef RT_USING_ADC1
    ADC1_CONFIG,
#endif
};

struct stm32_adc
{
    struct rt_i2c_config *config;
    struct rt_adc_device stm32_adc_device;
};

static struct stm32_adc stm32_adc_obj[sizeof(adc_config) / sizeof(adc_config[0])];

static rt_err_t stm32_adc_enabled(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled)
{
    ADC_HandleTypeDef *stm32_adc_handler;
    RT_ASSERT(device != RT_NULL);
    stm32_adc_handler = (ADC_HandleTypeDef *)device->user_data;

    rt_kprintf("%d: 0x%08x\r\n", __LINE__, stm32_adc_handler);

    if (enabled)
    {
        ADC_Enable(stm32_adc_handler);
    }
    else
    {
        ADC_Disable(stm32_adc_handler);
    }

    return RT_EOK;
}

static rt_uint32_t stm32_adc_get_channel(rt_uint32_t channel)
{
    rt_uint32_t stm32_channel = 0;

    switch (channel)
    {
    case  0:
        stm32_channel = ADC_CHANNEL_0;
        break;
    case  1:
        stm32_channel = ADC_CHANNEL_1;
        break;
    case  2:
        stm32_channel = ADC_CHANNEL_2;
        break;
    case  3:
        stm32_channel = ADC_CHANNEL_3;
        break;
    case  4:
        stm32_channel = ADC_CHANNEL_4;
        break;
    case  5:
        stm32_channel = ADC_CHANNEL_5;
        break;
    case  6:
        stm32_channel = ADC_CHANNEL_6;
        break;
    case  7:
        stm32_channel = ADC_CHANNEL_7;
        break;
    case  8:
        stm32_channel = ADC_CHANNEL_8;
        break;
    case  9:
        stm32_channel = ADC_CHANNEL_9;
        break;
    case 10:
        stm32_channel = ADC_CHANNEL_10;
        break;
    case 11:
        stm32_channel = ADC_CHANNEL_11;
        break;
    case 12:
        stm32_channel = ADC_CHANNEL_12;
        break;
    case 13:
        stm32_channel = ADC_CHANNEL_13;
        break;
    case 14:
        stm32_channel = ADC_CHANNEL_14;
        break;
    case 15:
        stm32_channel = ADC_CHANNEL_15;
        break;
    }

    return stm32_channel;
}

static rt_err_t stm32_get_adc_value(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value)
{
    ADC_ChannelConfTypeDef ADC_ChanConf;
    ADC_HandleTypeDef *stm32_adc_handler;

    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(value != RT_NULL);

    stm32_adc_handler = (ADC_HandleTypeDef *)device->user_data;

    rt_memset(&ADC_ChanConf, 0, sizeof(ADC_ChanConf));

    if (channel <= 18)
    {
        /* set stm32 ADC channel */
        ADC_ChanConf.Channel =  stm32_adc_get_channel(channel);
    }
    else
    {
        rt_kprintf("ADC channel must be between 0 and 18.");
        return -RT_ERROR;
    }

    ADC_ChanConf.Rank = 1;
    ADC_ChanConf.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
    ADC_ChanConf.Offset = 0;
    ADC_ChanConf.OffsetNumber = ADC_OFFSET_NONE;
    ADC_ChanConf.SingleDiff = LL_ADC_SINGLE_ENDED;
    HAL_ADC_ConfigChannel(stm32_adc_handler, &ADC_ChanConf);

    if (HAL_ADCEx_Calibration_Start(stm32_adc_handler, ADC_ChanConf.SingleDiff) != HAL_OK)
    {
        rt_kprintf("ADC calibration error!\n");
        return -RT_ERROR;
    }
    /* start ADC */
    HAL_ADC_Start(stm32_adc_handler);

    /* Wait for the ADC to convert */
    HAL_ADC_PollForConversion(stm32_adc_handler, 100);

    /* get ADC value */
    *value = (rt_uint32_t)HAL_ADC_GetValue(stm32_adc_handler);

    return RT_EOK;
}

rt_uint32_t rt_adc_read(rt_adc_device_t dev, rt_uint32_t channel)
{
    rt_uint32_t value;

    RT_ASSERT(dev);

    stm32_get_adc_value(dev, channel, &value);

    return value;
}

rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_uint32_t channel)
{
    rt_err_t result = RT_EOK;

    RT_ASSERT(dev);

    result = stm32_adc_enabled(dev, channel, RT_TRUE);

    return result;
}

rt_err_t rt_adc_disable(rt_adc_device_t dev, rt_uint32_t channel)
{
    rt_err_t result = RT_EOK;

    RT_ASSERT(dev);

    result = stm32_adc_enabled(dev, channel, RT_FALSE);

    return result;
}

struct rt_adc_device *rt_adc_device_find(const char *name)
{
    int i = 0;
    for (i = 0; i < sizeof(adc_config) / sizeof(adc_config[0]); i++)
    {
        if(rt_strncmp(stm32_adc_obj[i].config->name, name, RT_NAME_MAX) == 0)
        {
            return &stm32_adc_obj[i].stm32_adc_device;
        }
    }
    return RT_NULL;
}

static int rt_hw_adc_init(void)
{
    int i = 0;
    for (i = 0; i < sizeof(adc_config) / sizeof(adc_config[0]); i++)
    {
        stm32_adc_obj[i].config = &adc_config[i];
        stm32_adc_obj[i].stm32_adc_device.user_data = (uint8_t *)&stm32_adc_obj[i].config->ADC_Handler;
        rt_kprintf("%d: 0x%08x\r\n", __LINE__, &stm32_adc_obj[i].config->ADC_Handler);

        rt_kprintf("%d: 0x%08x\r\n", __LINE__, ADC1);
        rt_kprintf("%d: 0x%08x\r\n", __LINE__, stm32_adc_obj[i].config->ADC_Handler.Instance);

        if (HAL_ADC_Init(&stm32_adc_obj[i].config->ADC_Handler) != HAL_OK)
        {
            rt_kprintf("%s init failed", stm32_adc_obj[i].config->name);
            return -RT_ERROR;
        }
    }

    return RT_EOK;
}
INIT_APP_EXPORT(rt_hw_adc_init);

void adc_get_obj(void)
{
    int32_t value = 0;
    struct rt_adc_device *dev = RT_NULL;
    dev = rt_adc_device_find("adc1");
    if(dev == RT_NULL)
    {
        rt_kprintf("%s not found\r\n", "adc1");
        return;
    }
    else
    {
        rt_adc_enable(dev, 3);
        value = rt_adc_read(dev, 3);
        rt_kprintf("adc value: %d\r\n", value);
    }
}
MSH_CMD_EXPORT(adc_get_obj, adc_get_obj);

#endif /* RT_USING_ADC */

  • drv_adc.h实例:
代码语言:javascript
复制
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author            Notes
 * 2021-04-20     RiceChen      first version
 */

#ifndef __DRV_ADC_H__
#define __DRV_ADC_H__

#include <drv_common.h>
#include <board.h>
#include "adc.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifdef RT_USING_ADC1
#ifndef ADC1_CONFIG
#define ADC1_CONFIG                                                        \
    {                                                                      \
       .name                                   = "adc1",                        \
       .ADC_Handler.Instance                   = ADC1,                          \
       .ADC_Handler.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4,      \
       .ADC_Handler.Init.Resolution            = ADC_RESOLUTION_12B,            \
       .ADC_Handler.Init.DataAlign             = ADC_DATAALIGN_RIGHT,           \
       .ADC_Handler.Init.ScanConvMode          = DISABLE,                       \
       .ADC_Handler.Init.EOCSelection          = DISABLE,                       \
       .ADC_Handler.Init.ContinuousConvMode    = DISABLE,                       \
       .ADC_Handler.Init.NbrOfConversion       = 1,                             \
       .ADC_Handler.Init.DiscontinuousConvMode = DISABLE,                       \
       .ADC_Handler.Init.NbrOfDiscConversion   = 0,                             \
       .ADC_Handler.Init.ExternalTrigConv      = ADC_SOFTWARE_START,            \
       .ADC_Handler.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE, \
       .ADC_Handler.Init.DMAContinuousRequests = DISABLE,                       \
    }
#endif /* ADC1_CONFIG */
#endif /* RT_USING_ADC1 */

#ifdef __cplusplus
}
#endif

#endif /* __DRV_ADC_H__ */


编写 ADC 设备使用示例

代码语言:javascript
复制
void adc_test(void)
{
    int32_t value = 0;
    struct rt_adc_device *dev = RT_NULL;
    dev = rt_adc_device_find("adc1");
    if(dev == RT_NULL)
    {
        rt_kprintf("%s not found\r\n", "adc1");
        return;
    }
    else
    {
        rt_adc_enable(dev, 3);
        value = rt_adc_read(dev, 3);
        rt_kprintf("adc value: %d\r\n", value);
    }
}
MSH_CMD_EXPORT(adc_test, adc test);
  1. 实例代码运行现象:
代码语言:javascript
复制
msh >adc_test
adc value: 565
msh >

总结

  • 通过适配PIN设备接口,我们可以无缝对接到软件包的使用。
  • 对于低资源的芯片使用 Nano 并且能够使用 RT-THREAD 丰富的软件,无疑是一个非常完美的做法。也没有庞大的驱动框架。
  • 通过这样的方式,学习完 RT-THREAD Nano 在转移到 RT-THREAD 标准版的学习,更加简单方便。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么需要设备接口
  • 准备工作
  • ADC 设备接口
  • 适配 ADC 设备驱动接口
  • 编写 ADC 设备使用示例
  • 总结
相关产品与服务
物联网
腾讯连连是腾讯云物联网全新商业品牌,它涵盖一站式物联网平台 IoT Explorer,连连官方微信小程序和配套的小程序 SDK、插件和开源 App,并整合腾讯云内优势产品能力,如大数据、音视频、AI等。同时,它打通腾讯系 C 端内容资源,如QQ音乐、微信支付、微保、微众银行、医疗健康等生态应用入口。提供覆盖“云-管-边-端”的物联网基础设施,面向“消费物联”和 “产业物联”两大赛道提供全方位的物联网产品和解决方案,助力企业高效实现数字化转型。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档