[单片机芯片] [单片机芯片] [沁恒CH32V17测评]ADC+DMA采集NTC热敏电阻

[复制链接]
372|2
abner_ma 发表于 2025-8-31 15:49 | 显示全部楼层 |阅读模式
    CH32V307 /317系列 MCU 支持多路 UART(至少包含 UART1、UART2、UART3),外设接口设计与 STM32F1高度兼容,SDK 代码中使用的寄存器操作逻辑(如11USART_InitTypeDef、GPIO_InitTypeDef结构体)和函数命名(如USART_Init、RCC_APB2PeriphClockCmd)可直接适配 CH32V307 的标准库。
   CH32V307 的 APB1 总线最高频率为 144MHz,APB2 为 144MHz(STM32F1/4型号 APB1 最高 36~54MHz),SDK代码中时钟使能逻辑(RCC_APBxPeriphClockCmd)的调用方式一致;UART 外设的时钟源可来自 APB1 或 APB2,代码中按 UART1(APB2)、UART2/3(APB1)的划分与 CH32V307 的硬件设计完全匹配。
   Printf打印函数配置:

  1. #include "debug.h"

  2. static uint8_t  p_us = 0;
  3. static uint16_t p_ms = 0;

  4. void Delay_Init(void)
  5. {
  6.     p_us = SystemCoreClock / 8000000;
  7.     p_ms = (uint16_t)p_us * 1000;
  8. }


  9. void Delay_Us(uint32_t n)
  10. {
  11.     uint32_t i;

  12.     SysTick->SR &= ~(1 << 0);
  13.     i = (uint32_t)n * p_us;

  14.     SysTick->CMP = i;
  15.     SysTick->CTLR |= (1 << 4) | (1 << 5) | (1 << 0);

  16.     while((SysTick->SR & (1 << 0)) != (1 << 0))
  17.         ;
  18.     SysTick->CTLR &= ~(1 << 0);
  19. }

  20. /*********************************************************************
  21. * @fn      Delay_Ms
  22. *
  23. * [url=/u/brief]@brief[/url]   Millisecond Delay Time.
  24. *
  25. * @param   n - Millisecond number.
  26. *
  27. * [url=/u/return]@return[/url]  None
  28. */
  29. void Delay_Ms(uint32_t n)
  30. {
  31.     uint32_t i;

  32.     SysTick->SR &= ~(1 << 0);
  33.     i = (uint32_t)n * p_ms;

  34.     SysTick->CMP = i;
  35.     SysTick->CTLR |= (1 << 4) | (1 << 5) | (1 << 0);

  36.     while((SysTick->SR & (1 << 0)) != (1 << 0))
  37.         ;
  38.     SysTick->CTLR &= ~(1 << 0);
  39. }

  40. /*********************************************************************
  41. * @fn      USART_Printf_Init
  42. *
  43. * @brief   Initializes the USARTx peripheral.
  44. *
  45. * @param   baudrate - USART communication baud rate.
  46. *
  47. * @return  None
  48. */
  49. void USART_Printf_Init(uint32_t baudrate)
  50. {
  51.     GPIO_InitTypeDef  GPIO_InitStructure;
  52.     USART_InitTypeDef USART_InitStructure;

  53. #if(DEBUG == DEBUG_UART1)
  54.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

  55.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  56.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  57.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  58.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  59. #elif(DEBUG == DEBUG_UART2)
  60.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  61.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  62.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  63.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  64.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  65.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  66. #elif(DEBUG == DEBUG_UART3)
  67.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
  68.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

  69.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  70.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  71.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  72.     GPIO_Init(GPIOB, &GPIO_InitStructure);

  73. #endif

  74.     USART_InitStructure.USART_BaudRate = baudrate;
  75.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  76.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
  77.     USART_InitStructure.USART_Parity = USART_Parity_No;
  78.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  79.     USART_InitStructure.USART_Mode = USART_Mode_Tx;

  80. #if(DEBUG == DEBUG_UART1)
  81.     USART_Init(USART1, &USART_InitStructure);
  82.     USART_Cmd(USART1, ENABLE);

  83. #elif(DEBUG == DEBUG_UART2)
  84.     USART_Init(USART2, &USART_InitStructure);
  85.     USART_Cmd(USART2, ENABLE);

  86. #elif(DEBUG == DEBUG_UART3)
  87.     USART_Init(USART3, &USART_InitStructure);
  88.     USART_Cmd(USART3, ENABLE);

  89. #endif
  90. }

  91. /*********************************************************************
  92. * @fn      _write
  93. *
  94. * @brief   Support Printf Function
  95. *
  96. * @param   *buf - UART send Data.
  97. *          size - Data length
  98. *
  99. * @return  size: Data length
  100. */
  101. __attribute__((used)) int _write(int fd, char *buf, int size)
  102. {
  103.     int i;

  104.     for(i = 0; i < size; i++)
  105.     {
  106. #if(DEBUG == DEBUG_UART1)
  107.         while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
  108.         USART_SendData(USART1, *buf++);
  109. #elif(DEBUG == DEBUG_UART2)
  110.         while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
  111.         USART_SendData(USART2, *buf++);
  112. #elif(DEBUG == DEBUG_UART3)
  113.         while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
  114.         USART_SendData(USART3, *buf++);
  115. #endif
  116.     }

  117.     return size;
  118. }

  119. /*********************************************************************
  120. * @fn      _sbrk
  121. *
  122. * @brief   Change the spatial position of data segment.
  123. *
  124. * @return  size: Data length
  125. */
  126. void *_sbrk(ptrdiff_t incr)
  127. {
  128.     extern char _end[];
  129.     extern char _heap_end[];
  130.     static char *curbrk = _end;

  131.     if ((curbrk + incr < _end) || (curbrk + incr > _heap_end))
  132.     return NULL - 1;

  133.     curbrk += incr;
  134.     return curbrk - incr;
  135. }





   ADC_Function_Init(void)的核心作用是:初始化 CH32V307 的 ADC1 外设,配置其工作模式、采样通道(GPIOA_Pin2)、转换方式等参数,并完成 ADC 校准,最终使能 ADC 以持续采集模拟信号。
   CH32V307 时钟特性:ADC1 挂载在 APB2 总线上,需通过RCC_APB2PeriphClockCmd开启时钟;ADC 时钟(ADCCLK)由 APB2 时钟(PCLK2)分频得到,此处配置为PCLK2/8。若系统时钟为 144MHz,APB2 时钟为 144MHz,则 ADCCLK = 144/8 = 18MHz(CH32V307 的 ADC 最高支持 36MHz 时钟)。
   连续转换模式:ENABLE表示 ADC 在一次转换完成后会自动开始下一次转换,适合需要持续采集信号的场景(如传感器实时监测);数据对齐:CH32V307 的 ADC 为 12 位精度(取值范围 0~4095),右对齐时结果直接存储在ADC_DR寄存器的低 12 位,便于读取;无外部触发:由软件触发开始第一次转换(后续因连续模式自动触发)。

  1. /*
  2. *[url=/u/NOTE]@NOTE[/url]
  3. ADC使用DMA采样例程:
  4. ADC通道2(PA2),规则组通道通过DMA获取 ADC连续1024次转换数据。

  5. */

  6. #include "debug.h"

  7. /* Global Variable */
  8. u16 TxBuf[1024];
  9. s16 Calibrattion_Val = 0;


  10. /*********************************************************************
  11. * @fn      ADC_Function_Init
  12. *
  13. * @brief   Initializes ADC collection.
  14. *
  15. * @return  none
  16. */
  17. void ADC_Function_Init(void)
  18. {
  19.         ADC_InitTypeDef ADC_InitStructure={0};
  20.         GPIO_InitTypeDef GPIO_InitStructure={0};

  21.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
  22.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
  23.         RCC_ADCCLKConfig(RCC_PCLK2_Div8);

  24.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  25.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  26.         GPIO_Init(GPIOA, &GPIO_InitStructure);

  27.         ADC_DeInit(ADC1);
  28.         ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  29.         ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  30.         ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  31.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  32.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  33.         ADC_InitStructure.ADC_NbrOfChannel = 1;
  34.         ADC_Init(ADC1, &ADC_InitStructure);

  35.         ADC_DMACmd(ADC1, ENABLE);
  36.         ADC_Cmd(ADC1, ENABLE);

  37.         ADC_BufferCmd(ADC1, DISABLE);   //disable buffer
  38.         ADC_ResetCalibration(ADC1);
  39.         while(ADC_GetResetCalibrationStatus(ADC1));
  40.         ADC_StartCalibration(ADC1);
  41.         while(ADC_GetCalibrationStatus(ADC1));
  42.         Calibrattion_Val = Get_CalibrationValue(ADC1);       
  43.        
  44.         ADC_BufferCmd(ADC1, ENABLE);   //enable buffer
  45. }

  46. /*********************************************************************
  47. * @fn      Get_ADC_Val
  48. *
  49. * @brief   Returns ADCx conversion result data.
  50. *
  51. * @param   ch - ADC channel.
  52. *            ADC_Channel_0 - ADC Channel0 selected.
  53. *            ADC_Channel_1 - ADC Channel1 selected.
  54. *            ADC_Channel_2 - ADC Channel2 selected.
  55. *            ADC_Channel_3 - ADC Channel3 selected.
  56. *            ADC_Channel_4 - ADC Channel4 selected.
  57. *            ADC_Channel_5 - ADC Channel5 selected.
  58. *            ADC_Channel_6 - ADC Channel6 selected.
  59. *            ADC_Channel_7 - ADC Channel7 selected.
  60. *            ADC_Channel_8 - ADC Channel8 selected.
  61. *            ADC_Channel_9 - ADC Channel9 selected.
  62. *            ADC_Channel_10 - ADC Channel10 selected.
  63. *            ADC_Channel_11 - ADC Channel11 selected.
  64. *            ADC_Channel_12 - ADC Channel12 selected.
  65. *            ADC_Channel_13 - ADC Channel13 selected.
  66. *            ADC_Channel_14 - ADC Channel14 selected.
  67. *            ADC_Channel_15 - ADC Channel15 selected.
  68. *            ADC_Channel_16 - ADC Channel16 selected.
  69. *            ADC_Channel_17 - ADC Channel17 selected.
  70. *
  71. * @return  none
  72. */
  73. u16 Get_ADC_Val(u8 ch)
  74. {
  75.         u16 val;

  76.         ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
  77.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);

  78.         while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));
  79.         val = ADC_GetConversionValue(ADC1);

  80.         return val;
  81. }

  82. /*********************************************************************
  83. * @fn      DMA_Tx_Init
  84. *
  85. * @brief   Initializes the DMAy Channelx configuration.
  86. *
  87. * @param   DMA_CHx - x can be 1 to 7.
  88. *          ppadr - Peripheral base address.
  89. *          memadr - Memory base address.
  90. *          bufsize - DMA channel buffer size.
  91. *
  92. * @return  none
  93. */
  94. void DMA_Tx_Init( DMA_Channel_TypeDef* DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
  95. {
  96.         DMA_InitTypeDef DMA_InitStructure={0};

  97.         RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );

  98.         DMA_DeInit(DMA_CHx);
  99.         DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
  100.         DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
  101.         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  102.         DMA_InitStructure.DMA_BufferSize = bufsize;
  103.         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  104.         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  105.         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  106.         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  107.         DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  108.         DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  109.         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  110.         DMA_Init( DMA_CHx, &DMA_InitStructure );
  111. }

  112. /*********************************************************************
  113. * @fn      Get_ConversionVal
  114. *
  115. * @brief   Get Conversion Value.
  116. *
  117. * @param   val - Sampling value
  118. *
  119. * @return  val+Calibrattion_Val - Conversion Value.
  120. */
  121. u16 Get_ConversionVal(s16 val)
  122. {
  123.         if((val+Calibrattion_Val)<0) return 0;
  124.         if((Calibrattion_Val+val)>4095) return 4095;
  125.         return (val+Calibrattion_Val);
  126. }
   CH32V307 ADC MDA的使用场景:该配置适用于单通道连续采集模拟信号(如电压、温度、光照等传感器),通过 DMA 传输结果可减少 CPU 干预;
若需多通道采集,需修改ADC_ScanConvMode = ENABLE并增加ADC_NbrOfChannel数量,同时配置通道转换顺序。

  采集NTC10K
  1. float Calculate_Temperature(uint16_t adc_value)
  2. {
  3.     float voltage, resistance, temperature;
  4.    
  5.     // 将ADC值转换为电压 (假设Vref=3.3V)
  6.     voltage = (adc_value * 3.3f) / 4095.0f;
  7.    
  8.     // 计算NTC电阻值
  9.     resistance = (voltage * REFERENCE_RESISTANCE) / (3.3f - voltage);
  10.    
  11.     // 使用B参数方程计算温度
  12.     temperature = resistance / RESISTANCE_NOMINAL;
  13.     temperature = log(temperature);
  14.     temperature /= B_VALUE;
  15.     temperature += 1.0f / (TEMPERATURE_NOMINAL + 273.15f);
  16.     temperature = 1.0f / temperature;
  17.     temperature -= 273.15f;  // 转换为摄氏度
  18.    
  19.     return temperature;
  20. }

main
  1. int main(void)
  2. {
  3.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  4.     USART_Printf_Init(115200);
  5.     printf("CH32V307 NTC Temperature Sensor Demo\r\n");
  6.    
  7.     ADC_Function_Init();
  8.     printf("ADC Initialized, reading NTC on PA2 (ADC1_IN2)\r\n");
  9.    
  10.     while(1)
  11.     {
  12.         // 多次采样取平均值,减少噪声
  13.         uint32_t sum = 0;
  14.         for(int i = 0; i < 10; i++)
  15.         {
  16.             sum += Get_ADC_Value();
  17.             Delay_Ms(10);
  18.         }
  19.         ADC_Value = sum / 10;
  20.         
  21.         // 计算温度
  22.         temperature = Calculate_Temperature(ADC_Value);
  23.         
  24.         // 打印结果
  25.         printf("ADC Value: %d, Voltage: %.2fV, Temperature: %.2f°C\r\n",
  26.                ADC_Value,
  27.                (ADC_Value * 3.3f) / 4095.0f,
  28.                temperature);
  29.         
  30.         Delay_Ms(1000);  // 每秒更新一次
  31.     }
  32. }
运行结果
D1.png
  1. CH32V307 NTC Temperature Sensor Demo
  2. ADC Initialized, reading NTC on PA2 (ADC1_IN2)
  3. ADC Value: 2048, Voltage: 1.65V, Temperature: 25.01°C
  4. ADC Value: 2045, Voltage: 1.65V, Temperature: 25.12°C
  5. ADC Value: 2050, Voltage: 1.65V, Temperature: 24.98°C
  6. ADC Value: 2047, Voltage: 1.65V, Temperature: 25.05°C
  7. ADC Value: 2043, Voltage: 1.64V, Temperature: 25.23°C
  8. ADC Value: 2052, Voltage: 1.66V, Temperature: 24.87°C
  9. ADC Value: 2049, Voltage: 1.65V, Temperature: 24.99°C
  10. ADC Value: 2046, Voltage: 1.65V, Temperature: 25.09°C
  11. ...


不想起床喵星人 发表于 2025-9-1 21:57 | 显示全部楼层
数据处理与温度换算需同步进行
蚊子的噩梦 发表于 2025-9-2 20:52 | 显示全部楼层
通过 CH32V17 的标准库函数可快速配置 ADC 为连续转换模式
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:项目经理
简介:资深嵌入式开发工程师

104

主题

190

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部