STM32+HAL库ADC采样(采用内部基准电压)

本文介绍了STM32L476微控制器中如何利用内部基准电压VDDA和VREFINT_CAL进行实时测量,包括ADC的配置、单通道和多通道DMA方式的应用,以及在使用DMA时的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        当我们采用内部基准电压时,首先应了解下内部基准电压的用法,根据STM32L476参考手册我们得知:

解析:

VDDA:实际电压参考值,由上式计算得出,用来计算我们需要的采样电压值,下边会用到;

VREFINT_CAL:内部电压参考校准值,每个芯片都不一样,例如我的STM32L476则存在0x1FFF75AA-----0x1FFF75AB中,我们可以读出来:

#define VREFINT_CAL						(uint16_t)(*(__I uint16_t *)(0x1FFF75AA))

在数据手册中我们可以查到校准值的存储位置,如下图:

VERFINT_DATA:ADC转换后的实际VREFINT输出值,我们可以通过ADC的通道17读出来,该值在1.2V左右:

到这里我们就可以得到实时的参考电压值VDDA,既然确定了VDDA的值,我们便可以通过下式来计算我们实际测量的电压值:

解析:

VREFINT_CAL:固定值,我的芯片读出来是0x0681(上边有读取方法);

ADCx_DATA:ADC在通道X上测量的值(右对齐);

VREF_DATA:ADC转换后的实际VREFINT输出值,我们可以通过ADC的通道17读出来,该值在1.2V左右;

FULL_SCALE:ADC输出的最大数字值。例如12位分辨率,它将是212−1=4095或8位分辨率,28−1=255。

有了以上的基础,我们便可以通过配置CubeMX来产生我们需要的代码如下图:(大家可以根据自己的需求来选择相应的通道)

        配置为扫描模式,连续模式关闭,间断模式开启,间断分组为1,转换数设置为3,并配置每个RANK:

然后生成代码:

 /* ### - 2 - Start calibration ############################################ */
  if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK)
  {
    Error_Handler();
  }
  for(int i=0;i<3;i++)
		{  
			
			if(HAL_ADC_Start(&hadc1) != HAL_OK)//开始转换
			{
				/* Start Conversation Error */
				Error_Handler();
			}
			HAL_ADC_PollForConversion(&hadc1, 10);//等待常规组转换完成
  
			/* Check if the continous conversion of regular channel is finished */
			if ((HAL_ADC_GetState(&hadc1) & HAL_ADC_STATE_REG_EOC) == HAL_ADC_STATE_REG_EOC)
			{
				/*##-6- Get the converted value of regular channel  ########################*/
				V1[i] = HAL_ADC_GetValue(&hadc1);
				
			}
		}
		HAL_ADC_Stop(&hadc1);
		VI1[0] = 3.0*VREFINT_CAL*V1[0]/V1[2]/4095;
		VI1[1] = 3.0*VREFINT_CAL*V1[1]/V1[2]/4095;
		HAL_Delay(2000);

这里V1[0]、V1[1]、就是我通道6,通道7的测量值,V1[2]就是内部参考电压值,故根据:

便可以计算的出自己所需要的值。

多通道DMA方式配置:配置为扫描模式,连续模式,使能DMA连续请求,其他保持不变

生成代码:

uint32_t ADC_ConvertedValue[3];//定义数组用来接收数据
if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) !=  HAL_OK)//校准
		{ Error_Handler();}
		if (HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC_ConvertedValue,3 ) != HAL_OK)//启用ADC,开始常规组的转换,并通过DMA传输结果。       
		{ Error_Handler();}
		  while(!(__HAL_DMA_GET_FLAG(&hdma_adc1,DMA_FLAG_TC1))){;}//等待DMA转换完成
	  __HAL_DMA_CLEAR_FLAG(&hdma_adc1,DMA_FLAG_TC1);//清楚DMA标志位
		//	HAL_Delay (10);
		HAL_ADC_Stop_DMA(&hadc1);//停止ADC常规组转换,禁用ADC DMA传输
		HAL_Delay(2000);

这里有几点需要特别注意:

在使用DMA时:1、需要先初始化DMA,再初始化ADC;

        2、在CubeMX配置Data Width为WORD时,数组ADC_ConvertedValue应该定义为uint32_t,否则配置Data Width为HALF WORD时,数组ADC_ConvertedValue应该定义为uint16_t.

### STM32 CubeMX 中配置定时器触发 ADC 采样并使用 DMA 传输数据 #### 配置步骤概述 在 STM32CubeMX 工具中设置定时器触发 ADC 并通过 DMA 进行数据传输涉及几个关键组件的初始化和配置。这些组件包括 ADC、TIM 和 DMA 控制器。 #### 初始化 ADC 模块 为了使能 ADC 功能,在项目创建阶段需勾选相应的外设选项。进入 Pinout & Configuration 页面,找到 Analog 外设部分下的 `ADC` 节点展开它,并启用所需的模拟输入通道[^1]。 对于 ADC 的具体参数设定如下: - **Mode**: 设置为 Single-ended mode 单端模式; - **Resolution**: 可根据需求选择分辨率,默认情况下可能是 12-bit 或者其他值; - **Sampling Time**: 对于不同应用场合可以调整采样时间长短来平衡精度与速度之间的关系; - **Scan Conversion Mode**: 如果只用到单一通道则关闭此功能;若有多个通道参与转换,则开启扫描模式以便依次处理各个信道的数据; - **Continuous Conv. Mode**: 关闭连续转换模式,因为这里采用的是由外部事件(即定时器溢出)驱动的方式启动一次性的测量过程而不是周期性地自动运行; - **DMA Settings**: 启动 Direct Memory Access (DMA),指定好缓冲区地址以及每次完成一组完整的序列之后应该采取的动作——比如发出中断请求通知 CPU 去获取最新一批的结果。 ```c static void MX_ADC_Init(void) { /* USER CODE BEGIN ADC_Init 0 */ /* USER CODE END ADC_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; hadc.Instance = ADC1; hadc.Init.ScanConvMode = DISABLE; // Disable scan to work on single channel only. hadc.Init.ContinuousConvMode = DISABLE; hadc.Init.DiscontinuousConvMode = DISABLE; hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_TRGO; hadc.Init.DMAContinuousRequests = ENABLE; hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV; if (HAL_ADC_Init(&hadc) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES; if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) { Error_Handler(); } } ``` 上述代码片段展示了如何利用 HAL 函数对 ADC 实施基本配置,其中特别指定了 TRIGGER SOURCE 来源为 TIM1 的更新事件作为同步信号用于发起 A/D 转换操作。 #### 定时器 TIM 设定 接下来要做的就是建立一个合适的计数器实例用来定期激活前述已经准备好的模数变换电路。同样是在图形界面里定位至对应的 Timer 组件处做进一步编辑: - **Prescaler**:预分频系数决定了实际工作频率相对于 APBx 总线速率的比例因子; - **Counter Period** :最大计数值也即是说当达到该极限后会重新回到零点从而形成脉冲输出; - **Clock Division** : 分配给定时单元内部使用的时钟除法比例; - **Repetition Counter** : 若涉及到 PWM 输出等功能才需要用到重复次数属性; - **Output Compare/PWM Channel(s)**: 不必理会除非计划实现更复杂的功能像产生方波形之类; - **Master/Slave Mode** : 当存在多片级联情况时考虑主从架构设计; - **Synchronization Input Selection** : 此项关联到了前面提到过的 ADC 触发条件的选择上,应指向所期望的硬件资源例如 T1_TRGO 表明是从 TIM1 发射过来的同步脉冲[^2]。 ```c static void MX_TIM1_Init(void) { /* USER CODE BEGIN TIM1_Init 0 */ /* USER CODE END TIM1_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim1.Instance = TIM1; htim1.Init.Prescaler = 79; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 999; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) { Error_Handler(); } } void StartTimerForADCTrigger() { __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,PeriodValue); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); } ``` 以上给出了一段简单的例子说明怎样构建起基于定时机制的时间基底结构,注意这里的 Prescaler 参数应当依据系统核心振荡频率合理计算得出以确保最终得到恰当的工作节拍。 #### DMA 流程控制 最后一步便是安排好直接存储访问路径使得经过量化后的数字量能够被高效地搬移到内存空间内等待后续分析处理。这通常意味着要在软件层面预先分配一块足够大小的数组区域供存放即将流入的信息流,并且告诉 DMA 控制寄存器有关目标位置的具体坐标以及其他必要的细节描述如传输方向等。 ```c uint16_t aADCxConvertedData[BUFFER_SIZE]; /* Enable DMA Stream */ hdma_adc.Instance = DMA1_Stream0; hdma_adc.Init.Channel = DMA_CHANNEL_0; hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE; hdma_adc.Init.MemInc = DMA_MINC_ENABLE; hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; hdma_adc.Init.Mode = DMA_CIRCULAR; hdma_adc.Init.Priority = DMA_PRIORITY_HIGH; if (HAL_DMA_Init(&hdma_adc) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(&hadc,DMA_Handle,&hdma_adc); // 开启DMA循环模式下的一次性传输请求 HAL_ADC_Start_DMA(&hadc,(uint32_t*)aADCxConvertedData,BUFFER_SIZE); ``` 这段程序实现了将来自 ADC 接口的数据经由 DMA
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值