STM32开发,使用CUBEMX实现ADC采样以及二分法NTC温度采样

1 概述

1.1 资源概述

开发板:正点原子STM32F103 Nano开发板
CUBEMX版本:1.3.0
MDK版本:5.27
主控芯片型号:STM32F103RBT6
正点原子开发板

1.2 实现功能

1,适配正点原子STM32F103RB Nano开发板;
2,配置由CUBEMX生成;
3,采样AD的数值,并在串口上打印出来。
4,ADC运行时,LED0灯闪烁,当输出5次后,关闭ADC,同时LED0灯常亮。

2 硬件电路

3.3V通过电位器分压,最后送到芯片的PB1脚。调整VR1的值,即可调整ADC的值。
ADC采样电路图

3 程序实现

3.1 CUBEMX配置

1,时钟配置,CUBEMX配置为6分频,最后时钟频率为12MHz。ADC的采样频率不能高于14MHz。最短采样周期为(1.5+12.5)/14M=1uS
CUBEMX配置
2,配置ADC,采样独立模式,数据右对齐,单次采样,规则通道。
ADC配置

3.2 程序代码

1,main()函数代码如下,使用CUBEMX生成,删除掉无用的注释信息,并增加我们需要使用的代码。

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"

void LED0_ON(void);//函数在一开始进行申明,以;结束
void LED0_OFF(void);
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

int main(void)
{
	uint16_t t=0;
	uint16_t ADC_B=0;
	float ADC_D=0;

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  HAL_ADC_Start(&hadc1);//开启ADC

  for (t=0;t<10;t++)
  {

	t++;
	HAL_ADC_PollForConversion(&hadc1,10);    //等待转换完成,第二个参数表示超时时间,单位ms  
	if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))//Conversion data available on group regular
	{
		ADC_B = HAL_ADC_GetValue(&hadc1);
	}
	ADC_D= ADC_B*3.3/4096;
	HAL_Delay(500);	
	LED0_ON();  //调用函数,无void,如果带void,则系统则会再次认为是函数申明。
	HAL_Delay(500);	
	LED0_OFF();
	printf("STM32 ADC寄存器值为%d\r\n",ADC_B);//串口输出数据
	printf("STM32 ADC实际值值为%1.3fV\r\n",ADC_D);
  }
	HAL_ADC_Stop(&hadc1);//关闭adc
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
int fputc(int ch,FILE *f)
{
    
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;

}

void LED0_ON(void)//函数在结尾定义,没有;
	{
	GPIOC->BSRR = LED0_Pin;//打开LED0
	}
void LED0_OFF(void)
	{
	 GPIOC->BSRR = (uint32_t)LED0_Pin << 16u;//关闭LED0
	}


void Error_Handler(void)
{
}

2,ADC初始化函数如下。

void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig={0};//初始化函数声明
 
  
  /** Common config 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; //关闭扫描
  hadc1.Init.ContinuousConvMode = DISABLE;//连续转换模式无效
  hadc1.Init.DiscontinuousConvMode = DISABLE;//无效连续转换模式
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;//外部触发为软件启动
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;//数据右对齐
  hadc1.Init.NbrOfConversion = 1;//转换次数

  
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel 
  */
  sConfig.Channel = ADC_CHANNEL_9;//配置采样通道
  sConfig.Rank = ADC_REGULAR_RANK_1;//配置规则通道
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;//配置采样周期
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

程序中对开灯关灯的函数进行了简化,直接操作寄存器,没有使用HAL 函数,相比原函数,少了有效性判定,更加简洁,占用的空间更少,在项目中,建议前期可以使用HAL函数实现,后续将HAL函数剥离,使用寄存器的方式,这样做有几个好处:
①,消除HAL函数中可能存在的BUG。
②,提高程序效率,降低Flash使用空间,特别是当程序空间在64K,128K等这种两种芯片分段附近大小时,通过这种方式,有可能使芯片使用低一个档次的,实现更低的成本。
当然如果项目仅仅是实验性质的,并没有大批量生产的需求或者市场窗口周期极短,就没必要折腾了。HAL函数是最快最简洁的方式。连ST官网的新一代芯片后续都不计划提供标准库函数版本了,只提供HAL函数版本的支持。
自定义函数采用先定义后使用的方式,另外对于无形参无返回值的函数void LED0_ON(void)在主程序中的使用方式为 LED0_ON();不要画蛇添足添加void,添加void表明这个还是函数声明,而不是函数使用。
3,ADC函数说明
配置采样通道 ADC_CHANNEL_9和配置规则通道ADC_REGULAR_RANK_1示意如下。
输入通道和规则通道
扫描打开示意如下,本例子中只需要一个ADC,无需开启扫描。
扫描打开
单次转换模式和连续转换模式,
单次转换和连续转换
ADC有非常多种使用方法,其它使用方式参数其对应的参考手册。

4 实验结果

实验结果如下,符合预期。
实验结果

5、NTC温度采样

5.1 电路以及规格

NTC电阻选择10K(25℃),B=3950的电阻,与10K电阻串联,10K电阻上拉到3.3V,AD口采集NTC电阻上的电压。

分压电阻(KΩ)10
采样最大电压3.3
12位ADC最大分辨率0.000805664

电阻采样曲线如下图
在这里插入图片描述

5.2 算法以及源码

定义温度和AD值的两个数组,采集到的电阻电压后进行判定,若电压高于上限,低于下限,则返回对应的最高最低温度。若在范围内,则进行二分法查表,返回查表的下标值。对应找到另外一个数组中的温度值。在此程序中,由于温度采样是连续的,因此可以只定义一个AD采样数组,当返回下标时对其进行相应偏置即可,这样能省空间。
在进行比较时,3.1851后需要增加一个f,否则将会报错单精度和双精度不匹配的警告。
程序源码如下:

#include "temp.h"
#define  SIZE 166

static const int b[SIZE] = {
		-40, -39, -38, -37, -36, -35, -34, -33, -32, -31,
		-30, -29, -28, -27, -26, -25, -24, -23, -22, -21,
		-20, -19, -18, -17, -16, -15, -14, -13, -12, -11,
		-10, -9, -8, -7, -6, -5, -4, -3, -2, -1,
		0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
		10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
		20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
		30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
		40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
		50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
		60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
		70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
		80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
		90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
		100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
		110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
		120, 121, 122, 123, 124, 125 };//定义温度数组
static const float a[SIZE] = {
		3.1851 , 3.1794 , 3.1731 , 3.1663 , 3.1589 , 3.1510 , 3.1425 , 3.1335 , 3.1238 , 3.1135 , 
		3.1027 , 3.0912 , 3.0792 , 3.0665 , 3.0532 , 3.0394 , 3.0249 , 3.0098 , 2.9942 , 2.9780 , 
		2.9613 , 2.9443 , 2.9269 , 2.9088 , 2.8902 , 2.8710 , 2.8512 , 2.8309 , 2.8099 , 2.7883 , 
		2.7662 , 2.7434 , 2.7199 , 2.6959 , 2.6712 , 2.6459 , 2.6200 , 2.5934 , 2.5662 , 2.5384 , 
		2.5100 , 2.4802 , 2.4498 , 2.4189 , 2.3874 , 2.3554 , 2.3229 , 2.2898 , 2.2564 , 2.2225 , 
		2.1882 , 2.1536 , 2.1186 , 2.0833 , 2.0478 , 2.0120 , 1.9760 , 1.9399 , 1.9037 , 1.8674 , 
		1.8311 , 1.7947 , 1.7584 , 1.7222 , 1.6860 , 1.6500 , 1.6142 , 1.5785 , 1.5431 , 1.5080 , 
		1.4732 , 1.4387 , 1.4046 , 1.3708 , 1.3375 , 1.3045 , 1.2721 , 1.2400 , 1.2085 , 1.1775 , 
		1.1470 , 1.1170 , 1.0875 , 1.0586 , 1.0302 , 1.0025 , 0.9752 , 0.9486 , 0.9225 , 0.8970 , 
		0.8721 , 0.8478 , 0.8240 , 0.8008 , 0.7782 , 0.7561 , 0.7346 , 0.7136 , 0.6932 , 0.6734 , 
		0.6540 , 0.6352 , 0.6169 , 0.5991 , 0.5818 , 0.5650 , 0.5487 , 0.5328 , 0.5174 , 0.5025 , 
		0.4880 , 0.4739 , 0.4602 , 0.4470 , 0.4341 , 0.4216 , 0.4095 , 0.3978 , 0.3864 , 0.3754 , 
		0.3647 , 0.3544 , 0.3443 , 0.3346 , 0.3252 , 0.3160 , 0.3073 , 0.2988 , 0.2905 , 0.2825 , 
		0.2748 , 0.2672 , 0.2599 , 0.2528 , 0.2459 , 0.2392 , 0.2327 , 0.2264 , 0.2203 , 0.2143 , 
		0.2085 , 0.2031 , 0.1978 , 0.1926 , 0.1876 , 0.1826 , 0.1778 , 0.1731 , 0.1685 , 0.1640 , 
		0.1596 , 0.1554 , 0.1512 , 0.1472 , 0.1433 , 0.1394 , 0.1357 , 0.1321 , 0.1286 , 0.1252 , 
		0.1219 , 0.1187 , 0.1155 , 0.1125 , 0.1096 , 0.1067 , };//定义AD值数组

int tempDetect(float n)
{
	int s=0,m=0,e=SIZE;

	if(n < 0.1067f)
	  return 125;
	else if(n > 3.1851f)
	  return -40;
	else 
	{
		while(s < e)//二分法进行查找
		{
			m = ((s+e)>>1);
			if(n < a[m])
				s = m+1;
			else 
				e = m;
		}
		return b[s];
	}
}

头文件定义如下

#ifndef TEMP_H_
#define TEMP_H_

int tempDetect(float n); 

#endif

6 结果验证添加链接描述

在STM32F407单板上进行了验证,使用直流源直接输入AD口,模拟NTC电阻的温度变化,验证如图所示,结果与预期相符

在这里插入图片描述
对应的温度显示
在这里插入图片描述
实际的NTC温度检测,也是非常准的,室内空调开的26度。
在这里插入图片描述
在温度显示这个地方,需要对数据的正负进行判定,对应显示,同时显示前需要对显示区域进行清除。

		if(tempReal >= 0)
		{	
			LCD_Fill(30,190,240,320,WHITE);			//清除显示
			LCD_ShowxNum(30,190,tempReal,3,16,0); 	//显示实际温度
		}
		else
		{
			LCD_Fill(30,190,240,320,WHITE);
			LCD_ShowString(30,190,200,16,16,"-"); //显示负温度
			LCD_ShowxNum(38,190,-tempReal,2,16,0); //对负数进行取反显示
		}

7 程序地址

https://gitee.com/whaishen_whaishen/temp_detect.git

  • 4
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
STM32F103中,NTC(Negative Temperature Coefficient)是一种负温度系数热敏电阻。在代码中,NTC相关的结构体和宏定义被定义在\[3\]中。结构体NTC_t包含了采集的ADC值、温度、电压模拟量和热敏电阻值等信息。其中,ADC_Get_Temperature函数用于获取温度值。在初始化函数Peripheral_Set中,通过调用HAL_ADC_Start_DMA函数开启ADC1的DMA输出,实现NTC的采集\[2\]。而获取温度值的函数Get_Temperature_Value则通过二分法查找NTC_Table数组中对应的温度值,并计算出实际温度\[1\]。 #### 引用[.reference_title] - *1* *2* [STM32(F103ZETX)物联网项目学习笔记——MF52 珠状测温型 NTC 热敏电阻器(ADC、DMA)](https://blog.csdn.net/weixin_74135270/article/details/131520893)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [STM32开发(十二)STM32F103 功能应用 —— NTC 温度采集](https://blog.csdn.net/weixin_43564241/article/details/130003270)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值