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不能打印浮点型数据。


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

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

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

发表评论

登录后才能评论

评论列表(0条)

保存