一、ADC简介
ADC指的是模拟量转换成数字量,一般是将模拟的电压或者电流信号转换成数字量。这里要说明一下,在RT-Thread源码中所说的ADC设备指的一般是MCU内部的ADC模块(大家可以理解为能够直接操作ADC工作参数的模块)。笔者在项目开发过程中使用过一些检测身体特征的传感器(心率传感器),这些传感器本质上就是一个ADC转换成器,将采集的模拟类型的身体信号转换成数字量,然后经过像I2C或者SPI等通信协议传送给MCU。那么这些传感器,在RT-Thread内核中并不会将它们视为ADC设备,而是将它们视为I2C或者SPI设备(这具体根据传感器与MCU采用的通信方式来界定的)。
我还是采用从应用层一步一步向底层代码探究的方式剖析ADC设备源码。首先讲解应用层API接口的使用;接着讲解ADC框架的源码;最后讲解半导体厂商的底层实现。
ADC设备介绍
二、ADC设备源码剖析
应用程序通过 RT-Thread 提供的 ADC 设备管理接口来访问 ADC 硬件,相关接口如下所示:
1、rt_device_find
对于发现ADC设备和发现UART设备内部实现是一样的,只需要将ADC设备名称作为该函数的参数就可以了。具体的代码解析,可以看我写的关于UART设备的文章。
2、rt_adc_enable
a、应用层API接口
在读取 ADC 设备数据前需要先使能设备,通过如下函数使能设备:
b、框架实现
rt_adc_enable函数在RT-Thread内部框架实现代码如下:
rt_err_t rt_adc_enable(rt_adc_device_t dev, rt_uint32_t channel)
{
rt_err_t result = RT_EOK;
RT_ASSERT(dev);
if (dev->ops->enabled != RT_NULL)
{
result = dev->ops->enabled(dev, channel, RT_TRUE);//调用底层的ADC开启函数
}
else
{
result = -RT_ENOSYS;
}
return result;
}
该函数其实就是做一件事,当应用层调用该函数打开ADC设备时,该函数会执行最关键的一步:result = dev->ops->enabled(dev, channel, RT_TRUE); 。该函数会执行底层操作硬件的代码,打开ADC设备。比如说,如果我们使用的是STM32,那么该函数就会打开ADC片内外设。
c、底层实现(半导体或者硬件驱动需要做的事)
如果我们使用的是STM32芯片,那么当应用层调用函数rt_adc_enable打开ADC设备时,最终会执行的底层函数为stm32_adc_enabled。函数stm32_adc_enabled最终会操作硬件相关的代码打开ADC设备。挖坑你可能会产生疑问,我是怎么知道的会调用该函数。请不要着急,我会在本文章的后面进行讲解,我们先看一下该函数的具体实现,上代码:
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 = device->parent.user_data;
if (enabled)
{
#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0)
ADC_Enable(stm32_adc_handler);
#else
__HAL_ADC_ENABLE(stm32_adc_handler);//调用HAL库代码实现打开片内ADC模块
#endif
}
else
{
#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32G0)
ADC_Disable(stm32_adc_handler);
#else
__HAL_ADC_DISABLE(stm32_adc_handler);//调用HAL代码实现关闭片内ADC模块
#endif
}
return RT_EOK;
}
大家可以看到在函数stm32_adc_enabled有很多宏,这里会根据我们使用的具体的STM32平台执行不同的函数。我们假设使用的是STM32F4X系列的MCU,则需要执行的代码是__HAL_ADC_ENABLE(stm32_adc_handler); 。继续贴代码(代码所在文件:Stm32f4xx_hal_adc.h (bsp\stm32\libraries\stm32f4xx_hal\stm32f4xx_hal_driver\inc)):
/**
* @brief Enable the ADC peripheral.
* @param __HANDLE__ ADC handle
* @retval None
*/
#define __HAL_ADC_ENABLE(__HANDLE__) ((__HANDLE__)->Instance->CR2 |= ADC_CR2_ADON)
是不是感觉很简单,就一行代码。我们继续贴代码:
#define ADC_CR2_ADON_Pos (0U)
#define ADC_CR2_ADON_Msk (0x1UL << ADC_CR2_ADON_Pos) /*!< 0x00000001 */
#define ADC_CR2_ADON ADC_CR2_ADON_Msk /*!<A/D Converter ON / OFF *///从这里可以看出是开启ADC转换器
/**
* @brief ADC handle Structure definition
*/
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
typedef struct __ADC_HandleTypeDef
#else
typedef struct
#endif
{
ADC_TypeDef *Instance; /*!< Register base address */
ADC_InitTypeDef Init; /*!< ADC required parameters */
__IO uint32_t NbrOfCurrentConversionRank; /*!< ADC number of current conversion rank */
DMA_HandleTypeDef *DMA_Handle; /*!< Pointer DMA Handler */
HAL_LockTypeDef Lock; /*!< ADC locking object */
__IO uint32_t State; /*!< ADC communication state */
__IO uint32_t ErrorCode; /*!< ADC Error code */
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
void (* ConvCpltCallback)(struct __ADC_HandleTypeDef *ha