Rt

Rt,第1张

目录
  • 前言
  • 1 ADC设备驱动层
    • 1.1 ADC的配置参数
  • 2 ADC设备框架驱动层
  • 3 ADC应用层


前言

  ADC(Analog to Digital Converter)模数转换器,它的作用是将外界的模拟信号转化为离散数字信号,什么是外界的模拟信号呢?比较好理解,例如温湿度,无线信号的RSSI强度,声光,角度等等。


对于一些低端的微控制器,片上只能依靠另外的ADC转换芯片实现模拟信号的采集,对于一些好一点的单片机,基本上都拥有片上ADC资源。


ADC的转换一般过程是:取样->保持和量化->数字编码。



  本文为RT_thread操作系统下的ADC设备学习笔记,使用stm32来掌握RT_thread系统下的ADC设备编程思路,通过对ADC设备的学习,我们最终能使用IO设备接口来访问ADC。


1 ADC设备驱动层 1.1 ADC的配置参数

  对于STM32的片上ADC,配置参数主要有ADC时钟的分频因子,转换分辨率,数据对齐,采样的模式,规则通道的采样等级等等,这里不做记录,令ADC1的配置如下:

#define ADC1_CONFIG                                                 \
    {                                                               \
       .Instance                   = ADC1,                          \
       .Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4,      \
       .Init.Resolution            = ADC_RESOLUTION_12B,            \
       .Init.DataAlign             = ADC_DATAALIGN_RIGHT,           \
       .Init.ScanConvMode          = ADC_SCAN_DISABLE,              \
       .Init.EOCSelection          = ADC_EOC_SINGLE_CONV,           \
       .Init.LowPowerAutoWait      = DISABLE,                       \
       .Init.ContinuousConvMode    = DISABLE,                       \
       .Init.NbrOfConversion       = 1,                             \
       .Init.DiscontinuousConvMode = DISABLE,                       \
       .Init.NbrOfDiscConversion   = 1,                             \
       .Init.ExternalTrigConv      = ADC_SOFTWARE_START,            \
       .Init.DMAContinuousRequests = DISABLE,                       \
       .Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN,      \
    }
static ADC_HandleTypeDef adc_config[] =
{
#ifdef BSP_USING_ADC1
    ADC1_CONFIG,
#endif

  RT_thread中先定义一个adc的底层结构体,如下:

struct stm32_adc
{
    ADC_HandleTypeDef ADC_Handler;
    struct rt_adc_device stm32_adc_device;
};

  这里主要看第二个成员,RT_thread给ADC做了一个设备结构体,定义如下:

struct rt_adc_device
{
    struct rt_device parent;
    const struct rt_adc_ops *ops;
};
typedef struct rt_adc_device *rt_adc_device_t;

  ADC的操作函数只有两个,使能控制函数和转换控制函数:

struct rt_adc_ops
{
    rt_err_t (*enabled)(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled);
    rt_err_t (*convert)(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value);
};

(1)使能函数

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 = device->parent.user_data;

    RT_ASSERT(device != RT_NULL);
	/*直接调用HAL库的ADC使能/失能函数*/
    if (enabled)
    {
        ADC_Enable(stm32_adc_handler);
    }
    else
    {
        ADC_Disable(stm32_adc_handler);
    }

    return RT_EOK;
}

(2)转换函数

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 = device->parent.user_data;

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

    rt_memset(&ADC_ChanConf, 0, sizeof(ADC_ChanConf));
	//通道个数不超过18个
    if (channel <= 18)
    {
        /* 确认转换的ADC通道 */
        ADC_ChanConf.Channel =  stm32_adc_get_channel(channel);
    }
    else
    {
        LOG_E("ADC channel must be between 0 and 18.");
        return -RT_ERROR;
    }
    /*通道配置*/
    ADC_ChanConf.Rank = 1;//默认的转换优先等级是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);

    /* 开启转换 */
    HAL_ADC_Start(stm32_adc_handler);

    /* 等待转换完成 */
    HAL_ADC_PollForConversion(stm32_adc_handler, 100);

    /* 获取ADC转换结果 */
    *value = (rt_uint32_t)HAL_ADC_GetValue(stm32_adc_handler);

    return RT_EOK;
}

  下面看看RT_thread下的ADC初始化代码是怎么写的:

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

static int stm32_adc_init(void)
{
    int result = RT_EOK;
    /* 首先初始定义ADC设备的名字 */
    char name_buf[5] = {'a', 'd', 'c', '0', 0};
    int i = 0;
	/*遍历ADC设备,STM32最多有3个ADC模块,每个模块最多16/18个通道*/
    for (i = 0; i < sizeof(adc_config) / sizeof(adc_config[0]); i++)
    {
        /* ADC init */
        name_buf[3] = '0';
        stm32_adc_obj[i].ADC_Handler = adc_config[i];
#if defined(ADC1)
        if (stm32_adc_obj[i].ADC_Handler.Instance == ADC1)
        {	//如果是ADC1,设备名为adc1
            name_buf[3] = '1';
        }
#endif
#if defined(ADC2)
        if (stm32_adc_obj[i].ADC_Handler.Instance == ADC2)
        {
            name_buf[3] = '2';
        }
#endif
#if defined(ADC3)
        if (stm32_adc_obj[i].ADC_Handler.Instance == ADC3)
        {
            name_buf[3] = '3';
        }
#endif
		/*初始化ADC,完成adc配置*/
        if (HAL_ADC_Init(&stm32_adc_obj[i].ADC_Handler) != HAL_OK)
        {
            LOG_E("%s init failed", name_buf);
            result = -RT_ERROR;
        }
        else/*如果配置成功的话就将ADC注册到IO设备管理器中*/
        {
            /* 注册ADC设备 */
            if (rt_hw_adc_register(&stm32_adc_obj[i].stm32_adc_device, name_buf, &stm_adc_ops, &stm32_adc_obj[i].ADC_Handler) == RT_EOK)
            {
                LOG_D("%s init success", name_buf);
            }
            else
            {
                LOG_E("%s register failed", name_buf);
                result = -RT_ERROR;
            }
        }
    }

    return result;
}
2 ADC设备框架驱动层

  上面的注册ADC设备函数rt_hw_adc_register就属于ADC设备框架驱动层。


其函数原型如下:

rt_err_t rt_hw_adc_register(rt_adc_device_t device, const char *name, const struct rt_adc_ops *ops, const void *user_data)
{
    rt_err_t result = RT_EOK;
    RT_ASSERT(ops != RT_NULL && ops->convert != RT_NULL);

    device->parent.type = RT_Device_Class_Miscellaneous;
    device->parent.rx_indicate = RT_NULL;
    device->parent.tx_complete = RT_NULL;
    device->parent.init        = RT_NULL;
    device->parent.open        = RT_NULL;
    device->parent.close       = RT_NULL;
    device->parent.read        = _adc_read;
    device->parent.write       = RT_NULL;
    device->parent.control     = _adc_control;

    device->ops = ops;
    device->parent.user_data = (void *)user_data;

    result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR);

    return result;
}

  在ADC设备驱动层的使能函数和获取转换结果函数中,有这么一句:

ADC_HandleTypeDef *stm32_adc_handler = device->parent.user_data;

  所以在注册ADC设备的时候,函数rt_hw_adc_register的形参user_data必须选择传入&stm32_adc_obj[i].ADC_Handler



另外,上面的parent还赋值了两个函数,它们的原型如下:

static rt_size_t _adc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    rt_err_t result = RT_EOK;
    rt_size_t i;
    struct rt_adc_device *adc = (struct rt_adc_device *)dev;
    rt_uint32_t *value = (rt_uint32_t *)buffer;

    for (i = 0; i < size; i += sizeof(int))
    {
    	/*调用函数stm32_get_adc_value*/
        result = adc->ops->convert(adc, pos + i, value);
        if (result != RT_EOK)
        {
            return 0;
        }
        value++;
    }

    return i;
}

  注意:其中的形参pos是转换的通道。


static rt_err_t _adc_control(rt_device_t dev, int cmd, void *args)
{
    rt_err_t result = RT_EOK;
    rt_adc_device_t adc = (struct rt_adc_device *)dev;

    if (adc->ops->enabled == RT_NULL)
    {
        return -RT_ENOSYS;
    }
    if (cmd == RT_ADC_CMD_ENABLE)
    {
    	/*调用函数stm32_adc_enabled*/
        result = adc->ops->enabled(adc, (rt_uint32_t)args, RT_TRUE);
    }
    else if (cmd == RT_ADC_CMD_DISABLE)
    {
    	/*调用函数stm32_adc_disabled*/
        result = adc->ops->enabled(adc, (rt_uint32_t)args, RT_FALSE);
    }

    return result;
}
3 ADC应用层

  ADC的应用层相对简单,即使用IO设备函数接口访问ADC设备。


下面直接实践测试,使用ADC1的通道13来做测试,采集通道引脚的电压信号,打印到控制台。



线程如下:

#define ADC_transfer_channel 13
#define CONVERT_BITS (1<<12)
#define REFER_VOLTAGE  330
void ADCtest_task(void *parameter)
{
	rt_uint32_t l_raw_adc_val;
	rt_uint32_t vol;
	rt_device_t adc_device = rt_device_find("adc1");
	if(RT_NULL != adc_device)
	{
		LOG_D("adc1 device has exited");
		adc_device->control(adc_device, RT_ADC_CMD_ENABLE, RT_NULL);
	}
	while(1)
	{
		adc_device->read(adc_device, ADC_transfer_channel, &l_raw_adc_val, sizeof(l_raw_adc_val));
		vol = l_raw_adc_val * REFER_VOLTAGE / CONVERT_BITS;
		rt_kprintf("vol :%d.%02d \n", vol / 100, vol % 100);
		rt_thread_mdelay(500);
	}
}

打开串口助手可以看到:

  成功创建一个ADC线程。




  成功注册了adc1设备,并开始采集电压信号。



注意:函数rt_kprintf不能打印浮点型数据。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://outofmemory.cn/langs/562560.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-04-01
下一篇2022-04-01

随机推荐

  • 交房可以找第三方验房吗

    导读:交房的时候,人们常常会感到很迷茫,因为多数新房表面上看起来都是没有任何问题,可实际上,它有可能暗含很多隐患,那么交房可以找第三方验房吗?交房可以找第三方验房,而且如果找的是正规且专业的验房机构,可以帮助降低交房风险。因为很多业主都是初

    2022-12-29
    2400
  • 獭兔毛能水洗吗 獭兔毛可以水洗吗

    导读:獭兔毛能水洗吗?下面为大家带来介绍。1、獭兔毛不可以水洗。獭兔毛皮草忌讳水、热、阳光。如果一不小心弄湿了,需要立刻将上面的水珠抖掉并放在阴凉处风干,如果1、獭兔毛不可以水洗。獭兔毛皮草忌讳水、热、阳光。如果一不小心弄湿了,需要立刻将上

  • 洛阳桥位于何地

    很多地方都有自己特色的建筑物,并且很多地方因为某些特殊的建筑物而闻名,受到各地人的旅游,其中一个地方有座桥叫洛阳桥。那么洛阳桥位于何地呢?洛阳桥位于何地1、洛阳桥位于福建省泉州市,位于洛阳江水道之上。2、著名的跨海梁式大石桥,素有“海内第一

    2022-12-29
    1200
  • 托福考试被提醒三次怎么办

    托福考试被提醒三次可能是自己坐姿不规范被误认为作弊了,也可能是自己走神了,需要保持清醒,规范自己的坐姿,不要东张西望,认真答题即可。托福考试是一个由ETS测评研发的学术英语语言测试,托福考试通过考察听、说、读、写这4个技能方面体现参与者在学

  • 陨石用什么材质的好 哪种陨石材质好

    导读:陨石用什么材质的好?以下是小编为大家带来的介绍。1、硅化铁陨石更好。2、硅化铁陨石质地非常轻,只比玻璃略重,外表呈深褐色,通体布满致密的小气泡,外部有融壳,融壳上有1、硅化铁陨石更好。2、硅化铁陨石质地非常轻,只比玻璃略重,外表呈深褐

    2022-12-29
    1800
  • 林书豪NBA数据_林书豪生涯场均11分什么概念

    林书豪的成功,向外界证明了,亚裔后卫同样能在NBA站稳脚跟。作为一个落选秀,林书豪的生涯非常励志,他一共在NBA效力了9个赛季。从一个寂寂无名的边缘人,到轰动一时的林疯狂,再到后来拿到总冠军,林书豪的表现非常出色。你知道生涯场均11.6分是

    2022-12-29
    1600
  • 网红孙一宁个人资料年龄?网红孙一宁个人资料

    王思聪怒撕网红孙一宁,女方直播痛哭,大骂王思聪:疯狗一条6月15日一早,网红孙一宁在直播间号啕大哭,喊话王思聪不要逼她,随后又发文艾特王思聪大骂“疯狗”引发网友热议。孙一宁言辞激烈,直接放狠话“有本事让我死,没本事就闭嘴,点个赞就牛拉

    2022-12-29
    1300
  • 牛皮衣第一次穿能水洗吗 牛皮衣第一次穿能不能水洗

    导读:牛皮衣第一次穿能水洗吗?下面小编为大家整理推荐。1、牛牛皮衣第一次穿不能水洗。如果将牛皮衣放入水中,其皮革就会遭到破坏,出现变硬、变形、开裂的现象,影响正常使用,而1、牛牛皮衣第一次穿不能水洗。如果将牛皮衣放入水中,其皮革就会遭到破坏

    2022-12-29
    1600
  • 一年中几月份刷乳胶漆好

    导读:在刷油漆之前需要了解一下天气情况,一般来说,室内温度在5度左右是比较合适的,那么一年中四五月份刷乳胶漆好一些。一年中四五月份刷乳胶漆好一些,刷乳胶漆的条件是室内温度要在5摄氏度以上,天气要干燥晴朗。北方以5、6月份为宜,气温高,雨水少

    2022-12-29
    1900

发表评论

登录后才能评论

评论列表(0条)

    保存