使用 STM32F0 ADC 单独读取不同的输入

2024-04-11

STM32F072CBU 微控制器。

我有多个 ADC 输入,并且希望单独读取它们。 STMcubeMX 生成样板代码,假设我希望按顺序读取所有输入,但我无法弄清楚如何纠正这个问题。

这篇博文 http://blog.koepi.info/2015/05/stm32-adc-some-weird-behaviours.html表达了我遇到的同样的问题,但给出的解决方案似乎不起作用。每次转换时打开和关闭 ADC 与返回值中的错误相关。仅当我在 STMcubeMX 中配置单个 ADC 输入,然后在不取消初始化 ADC 的情况下进行轮询时,才会返回准确的读数。

cubeMX的adc_init函数:

/* ADC init function */
static void MX_ADC_Init(void)
{

  ADC_ChannelConfTypeDef sConfig;

    /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
    */
  hadc.Instance = ADC1;
  hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerAutoPowerOff = DISABLE;
  hadc.Init.ContinuousConvMode = DISABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.DMAContinuousRequests = DISABLE;
  hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  sConfig.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_1;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_2;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_3;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_4;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure for the selected ADC regular channel to be converted. 
    */
  sConfig.Channel = ADC_CHANNEL_VREFINT;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

main.c

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */
  //HAL_TIM_Base_Start_IT(&htim3);
  init_printf(NULL, putc_wrangler);
  HAL_ADCEx_Calibration_Start(&hadc);
  HAL_ADC_DeInit(&hadc); // ADC is initialized for every channel change
  schedule_initial_events();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  event_loop();
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  /* USER CODE END 3 */

}

我现在关闭 ADC 并重新初始化以更改通道的流程:

// Set up
  ADC_ChannelConfTypeDef channelConfig;

  channelConfig.SamplingTime = samplingT;
  channelConfig.Channel = sensorChannel;
  channelConfig.Rank = ADC_RANK_CHANNEL_NUMBER;

  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  if (HAL_ADC_ConfigChannel(&hadc, &channelConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

// Convert
  uint16_t retval;

  if (HAL_ADC_Start(&hadc) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  if (HAL_ADC_PollForConversion(&hadc, 1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  if (HAL_ADC_GetError(&hadc) != HAL_ADC_ERROR_NONE)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  retval = (uint16_t) HAL_ADC_GetValue(&hadc);

  if (HAL_ADC_Stop(&hadc) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

// Close
  HAL_ADC_DeInit(&hadc);

在这一点上,我不太确定是否有办法实现我想要的,STM32 似乎死心塌地地将活动 ADC 线放在常规组中并按顺序转换。


如果您想在单转换模式下读取多个 ADC 通道,则必须在每次读取之前更改通道设置,但不必重新初始化 ADC。只需执行以下操作,选择新通道(如果通道必须不同,您也可以更改采样时间,但通常可以相同),选择通道等级,然后调用 HAL_ADC_ConfigChannel 函数。此后您可以执行转换。

void config_ext_channel_ADC(uint32_t channel, boolean_t val)
{
  ADC_ChannelConfTypeDef sConfig;

  sConfig.Channel = channel;
  sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;

  if(True == val)
  {
    sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  }
  else
  {
    sConfig.Rank = ADC_RANK_NONE;
  }

  HAL_ADC_ConfigChannel(&hadc, &sConfig);
}

uint32_t r_single_ext_channel_ADC(uint32_t channel)
{
  uint32_t digital_result;

  config_ext_channel_ADC(channel, True);

  HAL_ADCEx_Calibration_Start(&hadc);

  HAL_ADC_Start(&hadc);
  HAL_ADC_PollForConversion(&hadc, 1000);
  digital_result = HAL_ADC_GetValue(&hadc);
  HAL_ADC_Stop(&hadc);

  config_ext_channel_ADC(channel, False);

  return digital_result;
}

使用示例:

#define SUPPLY_CURRENT  ADC_CHANNEL_5
#define BATTERY_VOLTAGE ADC_CHANNEL_6

uint16_t r_battery_voltage(uint16_t mcu_vcc)
{
  float vbat;
  uint16_t digital_val;

  digital_val = r_single_ext_channel_ADC(BATTERY_VOLTAGE);
  vbat = (mcu_vcc/4095.0) * digital_val;
  vbat = vbat * 2;         // 1/2 voltage divider

  return vbat;
}

uint16_t r_supply_current(uint16_t mcu_vcc)
{
  float v_sense, current;
  uint16_t digital_val;

  digital_val = r_single_ext_channel_ADC(SUPPLY_CURRENT);
  v_sense = (mcu_vcc/4095.0) * digital_val;
  current = v_sense * I_SENSE_GAIN;

  return current;
}

该代码用于 STM32F030。为了读取内部温度传感器和参考电压,需要设置与上述功能略有不同的版本,因为必须设置附加启用位。

void config_int_channel_ADC(uint32_t channel, boolean_t val)
{
  ADC_ChannelConfTypeDef sConfig;
  sConfig.Channel = channel;

  if(val == True)
  {
    if(channel == ADC_CHANNEL_VREFINT)
    {
      ADC->CCR |= ADC_CCR_VREFEN;
      hadc.Instance->CHSELR = (uint32_t)(ADC_CHSELR_CHSEL17);
    }
    else if(channel == ADC_CHANNEL_TEMPSENSOR)
    {
      ADC->CCR |= ADC_CCR_TSEN;
      hadc.Instance->CHSELR = (uint32_t)(ADC_CHSELR_CHSEL16);
    }

    sConfig.Rank          = ADC_RANK_CHANNEL_NUMBER;
    sConfig.SamplingTime  = ADC_SAMPLETIME_239CYCLES_5;
  }
  else if(val == False)
  {
    if(channel == ADC_CHANNEL_VREFINT)
    {
      ADC->CCR &= ~ADC_CCR_VREFEN;
      hadc.Instance->CHSELR = 0;
    }
    else if(channel == ADC_CHANNEL_TEMPSENSOR)
    {
      ADC->CCR &= ~ADC_CCR_TSEN;
      hadc.Instance->CHSELR = 0;
    }

    sConfig.Rank          = ADC_RANK_NONE;
    sConfig.SamplingTime  = ADC_SAMPLETIME_239CYCLES_5;
  }

  HAL_ADC_ConfigChannel(&hadc,&sConfig);
}

uint32_t r_single_int_channel_ADC(uint32_t channel)
{
  uint32_t digital_result;

  config_int_channel_ADC(channel, True);

  HAL_ADCEx_Calibration_Start(&hadc);

  HAL_ADC_Start(&hadc);
  HAL_ADC_PollForConversion(&hadc, 1000);
  digital_result = HAL_ADC_GetValue(&hadc);
  HAL_ADC_Stop(&hadc);

  config_int_channel_ADC(channel, False);

  return digital_result;
}

用于 MCU VDD 计算的内部参考电压示例:

#define VREFINT_CAL_ADDR   ((uint16_t*) ((uint32_t) 0x1FFFF7BA))

static float FACTORY_CALIB_VDD = 3.31;

uint16_t calculate_MCU_vcc()
{
  float analog_Vdd;
  uint16_t val_Vref_int = r_single_int_channel_ADC(ADC_CHANNEL_VREFINT);

  analog_Vdd = (FACTORY_CALIB_VDD * (*VREFINT_CAL_ADDR))/val_Vref_int;

  return analog_Vdd * 1000;
}

内部温度传感器读数:

#define TEMP30_CAL_ADDR  ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))

static float FACTORY_CALIB_VDD = 3.31;

float r_MCU_temp(uint16_t mcu_vcc)
{
  float temp;
  float slope = ((110.0 - 30.0)/((*TEMP110_CAL_ADDR) - (*TEMP30_CAL_ADDR)));

  uint16_t ts_data = r_single_int_channel_ADC(ADC_CHANNEL_TEMPSENSOR);

  temp = ((mcu_vcc/FACTORY_CALIB_VDD) * ts_data)/1000;
  temp = slope * (temp - (*TEMP30_CAL_ADDR)) + 30;

  return round_to(temp, 0);
}

请注意,您的 MCU 的校准数据地址可能有所不同,请查看数据表以获取更多信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 STM32F0 ADC 单独读取不同的输入 的相关文章

随机推荐

  • 如何更新猫鼬中的特定字段?

    我有一个数据集是 var JobSchema Schema candidates user type Schema Types ObjectId ref User status type String default In Progress
  • 如何使用私钥对字符串进行签名

    如何使用以下方式获取字符串的签名SHA1withRSA如果我已经拥有私钥byte or String 我想你所说的是你事先知道密钥对并且想用它来签名 验证 请看下面的代码 import java security KeyPair impor
  • 针对 SSE2 之前的处理器的 Java 运行时如何实现浮点基本运算?

    针对没有 SSE2 的 Intel 处理器的 Java 运行时如何处理浮点非规格化 当strictfp is set 即使 387 FPU 设置为 53 位精度 它也会保持超大的指数范围 强制检测每个中间结果的下溢 溢出 以及 使得很难避免
  • Python JSON AttributeError:“str”对象没有属性“read”

    我是 Python 初学者 Python 3 7 6 import json fil numbers json num with open fil r as file for obj in file num append json load
  • 如何用一个命令停止 mongo DB

    我需要能够在 cli 上启动 停止 MongoDB 开始非常简单 mongod 但要停止 mongo DB 我需要先运行 open mongo shell 然后键入两个命令 蒙戈 使用管理员 db shutdownServer 所以我不知道
  • shark不显示源代码

    我们正在尝试在 iPhone 应用程序上运行 shark 然而 在分析的样本中 它没有列出我们的任何应用程序功能 所有列出的都是库 当我们单击其中任何一个时 汇编代码都是可见的 大多数网站在构建应用程序时都会提到 生成调试符号 选项 我也找
  • 包括使用 Lambda 表达式

    在基于字符串的重载中Include我们指定包含一个集合 然后简单地通过以正确的顺序指定相关导航属性来包含下一层的引用 query Include Level1Collection Level2Reference 但是为什么当使用重载时Inc
  • Android 的自定义类加载器?

    我正在编写一个仪器库 我想在桌面和移动设备 Android 上使用它 它的功能是 公开一个带有单个参数的 main 即目标类的 main 安装一个类加载器 在加载所有类时拦截它们并对其进行检测 Like so Expects args 0
  • Electron打包后不支持ES6

    我正在使用各种ES6语法 http es6 features org 例如importETC React https reactjs org code JSX https reactjs org docs introducing jsx h
  • NEHotspotConfigurationManager 收到此警报:“无法加入网络<网络名称>”,而错误为零

    所以我试图通过关闭器监视连接状态 func reconnect success escaping gt Void failure escaping gt Void let manager NEHotspotConfigurationMana
  • 有没有办法摆脱 git status 中的帮助消息?

    Changes not staged for commit use git add
  • 如何从 Instagram API 获取访问令牌

    我一直在关注 Instagram 的基本指南https developers facebook com docs instagram basic display api guides getting access tokens and pe
  • 删除 t-sql 中所有大表的最佳方法是什么?

    我们遇到了一个有点奇怪的情况 基本上 我们的一个数据库中有两个表 其中包含大量我们不需要或不关心的日志信息 部分原因是我们的磁盘空间不足 我正在尝试清理表 但这需要很长时间 在周末运行后仍然有 57 000 000 多条记录 而这只是第一个
  • PageView 内的 InteractiveViewer

    我正在创建一个包含图像列表的 PageView 并且我想向每个图像添加 InteractiveViewer 以便可以调整其大小以查看详细信息 这是我写的 PageView builder dragStartBehavior DragStar
  • 将 numpy 数组转换为十六进制字节数组

    我想在 python 2 7 中将 numpy 数组转换为字节串 比如说我的 numpy 数组a是一个简单的2x2数组 看起来像这样 1 10 16 255 我的问题是 如何将此数组转换为字节字符串或字节数组 输出如下 x01 x0A x1
  • 如何使用webpack导入静态url

    如何使用 webpack 导入静态 url index js import http google com myscript js 确实不清楚你想做什么 但总的来说你有几个选择 预先下载脚本或通过 NPM 安装 这可能是处理外部依赖关系的首
  • Ruby on Rails robots.txt 文件夹

    我即将启动 Ruby on Rails 应用程序 作为最后一个任务 我想设置机器人 txt文件 我找不到有关如何为 Rails 应用程序正确编写路径的信息 起始路径是否始终是 Ruby on Rails 应用程序或应用程序文件夹的根路径 那
  • 启用 iCloud 时 iOS 应用程序在首次启动时冻结

    我在 iOS 应用程序中启用了 iCloud 并且在首次启动应用程序时 当我按下应用程序中的任何视图时 应用程序会冻结大约 5 秒 我跟着this http timroadley com 2012 04 03 core data in ic
  • GitHub - 当非默认分支与主分支合并时,PR 链接的问题不会被关闭

    I have main作为默认分支和dev作为非默认 我创建了一个问题和一个 PR 以从新的合并temp分支到dev分支 在公关中 我曾提到过Resolves 1 当我将 PR 合并到dev分支 问题 1不会关闭 因为它是非默认分支 然后我
  • 使用 STM32F0 ADC 单独读取不同的输入

    STM32F072CBU 微控制器 我有多个 ADC 输入 并且希望单独读取它们 STMcubeMX 生成样板代码 假设我希望按顺序读取所有输入 但我无法弄清楚如何纠正这个问题 这篇博文 http blog koepi info 2015