准备工作
使用 RT-Thread Studio 建立一个 STM32L431RCT6 的 RT-Thread Nano 基础工程。
基础工程创建可参考:在 RT-Thread Studio 上使用 RT-Thread Nano
ADC 设备接口
在 RT-Thread 标准版中,ADC设备驱动提供了一套设备管理接口来访问 ADC,用户程序可以直接使用该 API 操作 ADC 的功能,设备管理接口如下:
「函数」 | 「描述」 |
---|
rt_device_find() | 根据 ADC 设备名称查找设备获取设备句柄 |
rt_adc_enable() | 使能 ADC 设备 |
rt_adc_read() | 读取 ADC 设备数据 |
rt_adc_disable() | 关闭 ADC 设备 |
由于 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 设备 |
对于 RT-Thread Nano,只需要适配如上这套 API,便可简单修改后使用 RT-Thread 丰富软件包功能。
适配 ADC 设备驱动接口
复制 RT-Thread 完整版工程中的 adc.h 文件(路径:rt-thread\components\drivers\include\drivers\adc.h)到我们准备好的 STM32L431RCT6 的 RT-Thread Nano 基础工程中。
由于 RT-Thread Nano 没有驱动框架,所以我们要把 adc.h 中有关完整版的内容去掉。整理完之后的 adc.h 文件如下:
/*
?*?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__?*/
我们需要适配如上4个 ADC 设备 API,参考实例:drv_adc.c 和 drv_adc.h。
/*
?*?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?*/
/*
?*?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 设备使用示例
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);
实例代码运行现象:
msh?>adc_test
adc?value:?565
msh?>
总结
通过适配PIN设备接口,我们可以无缝对接到软件包的使用。
对于低资源的芯片使用 Nano 并且能够使用 RT-THREAD 丰富的软件,无疑是一个非常完美的做法。也没有庞大的驱动框架。
通过这样的方式,学习完 RT-THREAD Nano 在转移到 RT-THREAD 标准版的学习,更加简单方便。
关注微信公众号『Rice嵌入式开发技术分享』,后台回复“微信”添加作者微信,备注”入群“,便可邀请进入技术交流群。