你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

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

[复制链接]
STMCU小助手 发布时间:2022-5-5 17:03
1 概述
1.1 资源概述

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

~TQHHU)JUR0{TD$LBKR`XB1.png

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

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

5[9$VD7O8R[UN8MG_ELTKJ6.png

3 程序实现
3.1 CUBEMX配置

1,时钟配置,CUBEMX配置为6分频,最后时钟频率为12MHz。ADC的采样频率不能高于14MHz。最短采样周期为(1.5+12.5)/14M=1uS

WC)VW~KMJM4L8VBTPDSBQ03.png

2,配置ADC,采样独立模式,数据右对齐,单次采样,规则通道。
WO(IPI{~DRVQEDK4}BX3ENB.png

3.2 程序代码

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

  1. /* Includes ------------------------------------------------------------------*/
  2. #include "main.h"
  3. #include "adc.h"
  4. #include "usart.h"
  5. #include "gpio.h"
  6. #include "stdio.h"

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

  10. /* Private function prototypes -----------------------------------------------*/
  11. void SystemClock_Config(void);

  12. int main(void)
  13. {
  14.         uint16_t t=0;
  15.         uint16_t ADC_B=0;
  16.         float ADC_D=0;

  17.   HAL_Init();

  18.   SystemClock_Config();

  19.   MX_GPIO_Init();
  20.   MX_ADC1_Init();
  21.   MX_USART1_UART_Init();
  22.   HAL_ADC_Start(&hadc1);//开启ADC

  23.   for (t=0;t<10;t++)
  24.   {

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

  41. void SystemClock_Config(void)
  42. {
  43.   RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  44.   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  45.   RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  46.   /** Initializes the CPU, AHB and APB busses clocks
  47.   */
  48.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  49.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  50.   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  51.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  52.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  53.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  54.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  55.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  56.   {
  57.     Error_Handler();
  58.   }
  59.   /** Initializes the CPU, AHB and APB busses clocks
  60.   */
  61.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  62.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  63.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  64.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  65.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  66.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  67.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  68.   {
  69.     Error_Handler();
  70.   }
  71.   PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  72.   PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  73.   if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  74.   {
  75.     Error_Handler();
  76.   }
  77. }

  78. /* USER CODE BEGIN 4 */
  79. int fputc(int ch,FILE *f)
  80. {

  81.         HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
  82.         return ch;

  83. }

  84. void LED0_ON(void)//函数在结尾定义,没有;
  85.         {
  86.         GPIOC->BSRR = LED0_Pin;//打开LED0
  87.         }
  88. void LED0_OFF(void)
  89.         {
  90.          GPIOC->BSRR = (uint32_t)LED0_Pin << 16u;//关闭LED0
  91.         }


  92. void Error_Handler(void)
  93. {
  94. }
复制代码

2,ADC初始化函数如下。

  1. void MX_ADC1_Init(void)
  2. {
  3.   ADC_ChannelConfTypeDef sConfig={0};//初始化函数声明


  4.   /** Common config
  5.   */
  6.   hadc1.Instance = ADC1;
  7.   hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; //关闭扫描
  8.   hadc1.Init.ContinuousConvMode = DISABLE;//连续转换模式无效
  9.   hadc1.Init.DiscontinuousConvMode = DISABLE;//无效连续转换模式
  10.   hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;//外部触发为软件启动
  11.   hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;//数据右对齐
  12.   hadc1.Init.NbrOfConversion = 1;//转换次数


  13.   if (HAL_ADC_Init(&hadc1) != HAL_OK)
  14.   {
  15.     Error_Handler();
  16.   }
  17.   /** Configure Regular Channel
  18.   */
  19.   sConfig.Channel = ADC_CHANNEL_9;//配置采样通道
  20.   sConfig.Rank = ADC_REGULAR_RANK_1;//配置规则通道
  21.   sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;//配置采样周期
  22.   if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  23.   {
  24.     Error_Handler();
  25.   }

  26. }
复制代码

程序中对开灯关灯的函数进行了简化,直接操作寄存器,没有使用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示意如下。

Q3WOZEA)8Z9AK284MW1BQHU.png

扫描打开示意如下,本例子中只需要一个ADC,无需开启扫描。

GT25UO~@~_IL(@[HCHEKW56.png

单次转换模式和连续转换模式,

RYKNY1W2]449%VT6DDXWJ.png

ADC有非常多种使用方法,其它使用方式参数其对应的参考手册。

4 实验结果

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

%N}M0$YUH6Y$JNHARK2WL`P.png

5、NTC温度采样
5.1 电路以及规格
NTC电阻选择10K(25℃),B=3950的电阻,与10K电阻串联,10K电阻上拉到3.3V,AD口采集NTC电阻上的电压。

UY_32P5ELO`~D`AL3]4B)]M.png

电阻采样曲线如下图

)R%9NT{PY]E5Z]1KX`1K741.png

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

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

  39. int tempDetect(float n)
  40. {
  41.         int s=0,m=0,e=SIZE;

  42.         if(n < 0.1067f)
  43.           return 125;
  44.         else if(n > 3.1851f)
  45.           return -40;
  46.         else
  47.         {
  48.                 while(s < e)//二分法进行查找
  49.                 {
  50.                         m = ((s+e)>>1);
  51.                         if(n < a[m])
  52.                                 s = m+1;
  53.                         else
  54.                                 e = m;
  55.                 }
  56.                 return b<strike>;
  57.         }
  58. }
  59. </strike>
复制代码

头文件定义如下

  1. #ifndef TEMP_H_
  2. #define TEMP_H_

  3. int tempDetect(float n);

  4. #endif
复制代码

6 结果验证添加链接描述

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

2DC6UT(PR6IQ`A6U40F}K_0.png

对应的温度显示
3H%7%]U6V~~_I}Z(}]YM8G7.png

实际的NTC温度检测,也是非常准的,室内空调开的26度。

20210901220454384.jpg

在温度显示这个地方,需要对数据的正负进行判定,对应显示,同时显示前需要对显示区域进行清除。

  1.                 if(tempReal >= 0)
  2.                 {        
  3.                         LCD_Fill(30,190,240,320,WHITE);                        //清除显示
  4.                         LCD_ShowxNum(30,190,tempReal,3,16,0);         //显示实际温度
  5.                 }
  6.                 else
  7.                 {
  8.                         LCD_Fill(30,190,240,320,WHITE);
  9.                         LCD_ShowString(30,190,200,16,16,"-"); //显示负温度
  10.                         LCD_ShowxNum(38,190,-tempReal,2,16,0); //对负数进行取反显示
  11.                 }
复制代码



收藏 评论0 发布时间:2022-5-5 17:03

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版