IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> Rt_threadのADC设备学习笔记 -> 正文阅读

[嵌入式]Rt_threadの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不能打印浮点型数据。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-16 22:35:59  更:2022-03-16 22:41:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:33:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码