[单片机芯片] [沁恒CH32V17测评]ADC+DMA采集NTC热敏电阻
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打印函数配置:
#include "debug.h"
static uint8_tp_us = 0;
static uint16_t p_ms = 0;
void Delay_Init(void)
{
p_us = SystemCoreClock / 8000000;
p_ms = (uint16_t)p_us * 1000;
}
void Delay_Us(uint32_t n)
{
uint32_t i;
SysTick->SR &= ~(1 << 0);
i = (uint32_t)n * p_us;
SysTick->CMP = i;
SysTick->CTLR |= (1 << 4) | (1 << 5) | (1 << 0);
while((SysTick->SR & (1 << 0)) != (1 << 0))
;
SysTick->CTLR &= ~(1 << 0);
}
/*********************************************************************
* @fn Delay_Ms
*
* @brief Millisecond Delay Time.
*
* @param n - Millisecond number.
*
* @returnNone
*/
void Delay_Ms(uint32_t n)
{
uint32_t i;
SysTick->SR &= ~(1 << 0);
i = (uint32_t)n * p_ms;
SysTick->CMP = i;
SysTick->CTLR |= (1 << 4) | (1 << 5) | (1 << 0);
while((SysTick->SR & (1 << 0)) != (1 << 0))
;
SysTick->CTLR &= ~(1 << 0);
}
/*********************************************************************
* @fn USART_Printf_Init
*
* @brief Initializes the USARTx peripheral.
*
* @param baudrate - USART communication baud rate.
*
* @returnNone
*/
void USART_Printf_Init(uint32_t baudrate)
{
GPIO_InitTypeDefGPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
#if(DEBUG == DEBUG_UART1)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
#elif(DEBUG == DEBUG_UART2)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
#elif(DEBUG == DEBUG_UART3)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
#endif
USART_InitStructure.USART_BaudRate = baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
#if(DEBUG == DEBUG_UART1)
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
#elif(DEBUG == DEBUG_UART2)
USART_Init(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
#elif(DEBUG == DEBUG_UART3)
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE);
#endif
}
/*********************************************************************
* @fn _write
*
* @brief Support Printf Function
*
* @param *buf - UART send Data.
* size - Data length
*
* @returnsize: Data length
*/
__attribute__((used)) int _write(int fd, char *buf, int size)
{
int i;
for(i = 0; i < size; i++)
{
#if(DEBUG == DEBUG_UART1)
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
USART_SendData(USART1, *buf++);
#elif(DEBUG == DEBUG_UART2)
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
USART_SendData(USART2, *buf++);
#elif(DEBUG == DEBUG_UART3)
while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
USART_SendData(USART3, *buf++);
#endif
}
return size;
}
/*********************************************************************
* @fn _sbrk
*
* @brief Change the spatial position of data segment.
*
* @returnsize: Data length
*/
void *_sbrk(ptrdiff_t incr)
{
extern char _end[];
extern char _heap_end[];
static char *curbrk = _end;
if ((curbrk + incr < _end) || (curbrk + incr > _heap_end))
return NULL - 1;
curbrk += incr;
return curbrk - incr;
}
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 位,便于读取;无外部触发:由软件触发开始第一次转换(后续因连续模式自动触发)。
/*
*@NOTE
ADC使用DMA采样例程:
ADC通道2(PA2),规则组通道通过DMA获取 ADC连续1024次转换数据。
*/
#include "debug.h"
/* Global Variable */
u16 TxBuf;
s16 Calibrattion_Val = 0;
/*********************************************************************
* @fn ADC_Function_Init
*
* @brief Initializes ADC collection.
*
* @returnnone
*/
void ADC_Function_Init(void)
{
ADC_InitTypeDef ADC_InitStructure={0};
GPIO_InitTypeDef GPIO_InitStructure={0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_BufferCmd(ADC1, DISABLE); //disable buffer
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
Calibrattion_Val = Get_CalibrationValue(ADC1);
ADC_BufferCmd(ADC1, ENABLE); //enable buffer
}
/*********************************************************************
* @fn Get_ADC_Val
*
* @brief Returns ADCx conversion result data.
*
* @param ch - ADC channel.
* ADC_Channel_0 - ADC Channel0 selected.
* ADC_Channel_1 - ADC Channel1 selected.
* ADC_Channel_2 - ADC Channel2 selected.
* ADC_Channel_3 - ADC Channel3 selected.
* ADC_Channel_4 - ADC Channel4 selected.
* ADC_Channel_5 - ADC Channel5 selected.
* ADC_Channel_6 - ADC Channel6 selected.
* ADC_Channel_7 - ADC Channel7 selected.
* ADC_Channel_8 - ADC Channel8 selected.
* ADC_Channel_9 - ADC Channel9 selected.
* ADC_Channel_10 - ADC Channel10 selected.
* ADC_Channel_11 - ADC Channel11 selected.
* ADC_Channel_12 - ADC Channel12 selected.
* ADC_Channel_13 - ADC Channel13 selected.
* ADC_Channel_14 - ADC Channel14 selected.
* ADC_Channel_15 - ADC Channel15 selected.
* ADC_Channel_16 - ADC Channel16 selected.
* ADC_Channel_17 - ADC Channel17 selected.
*
* @returnnone
*/
u16 Get_ADC_Val(u8 ch)
{
u16 val;
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));
val = ADC_GetConversionValue(ADC1);
return val;
}
/*********************************************************************
* @fn DMA_Tx_Init
*
* @brief Initializes the DMAy Channelx configuration.
*
* @param DMA_CHx - x can be 1 to 7.
* ppadr - Peripheral base address.
* memadr - Memory base address.
* bufsize - DMA channel buffer size.
*
* @returnnone
*/
void DMA_Tx_Init( DMA_Channel_TypeDef* DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
{
DMA_InitTypeDef DMA_InitStructure={0};
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );
DMA_DeInit(DMA_CHx);
DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = bufsize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init( DMA_CHx, &DMA_InitStructure );
}
/*********************************************************************
* @fn Get_ConversionVal
*
* @brief Get Conversion Value.
*
* @param val - Sampling value
*
* @returnval+Calibrattion_Val - Conversion Value.
*/
u16 Get_ConversionVal(s16 val)
{
if((val+Calibrattion_Val)<0) return 0;
if((Calibrattion_Val+val)>4095) return 4095;
return (val+Calibrattion_Val);
} CH32V307 ADC MDA的使用场景:该配置适用于单通道连续采集模拟信号(如电压、温度、光照等传感器),通过 DMA 传输结果可减少 CPU 干预;
若需多通道采集,需修改ADC_ScanConvMode = ENABLE并增加ADC_NbrOfChannel数量,同时配置通道转换顺序。
采集NTC10K
float Calculate_Temperature(uint16_t adc_value)
{
float voltage, resistance, temperature;
// 将ADC值转换为电压 (假设Vref=3.3V)
voltage = (adc_value * 3.3f) / 4095.0f;
// 计算NTC电阻值
resistance = (voltage * REFERENCE_RESISTANCE) / (3.3f - voltage);
// 使用B参数方程计算温度
temperature = resistance / RESISTANCE_NOMINAL;
temperature = log(temperature);
temperature /= B_VALUE;
temperature += 1.0f / (TEMPERATURE_NOMINAL + 273.15f);
temperature = 1.0f / temperature;
temperature -= 273.15f;// 转换为摄氏度
return temperature;
}
main
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
USART_Printf_Init(115200);
printf("CH32V307 NTC Temperature Sensor Demo\r\n");
ADC_Function_Init();
printf("ADC Initialized, reading NTC on PA2 (ADC1_IN2)\r\n");
while(1)
{
// 多次采样取平均值,减少噪声
uint32_t sum = 0;
for(int i = 0; i < 10; i++)
{
sum += Get_ADC_Value();
Delay_Ms(10);
}
ADC_Value = sum / 10;
// 计算温度
temperature = Calculate_Temperature(ADC_Value);
// 打印结果
printf("ADC Value: %d, Voltage: %.2fV, Temperature: %.2f°C\r\n",
ADC_Value,
(ADC_Value * 3.3f) / 4095.0f,
temperature);
Delay_Ms(1000);// 每秒更新一次
}
}
运行结果
CH32V307 NTC Temperature Sensor Demo
ADC Initialized, reading NTC on PA2 (ADC1_IN2)
ADC Value: 2048, Voltage: 1.65V, Temperature: 25.01°C
ADC Value: 2045, Voltage: 1.65V, Temperature: 25.12°C
ADC Value: 2050, Voltage: 1.65V, Temperature: 24.98°C
ADC Value: 2047, Voltage: 1.65V, Temperature: 25.05°C
ADC Value: 2043, Voltage: 1.64V, Temperature: 25.23°C
ADC Value: 2052, Voltage: 1.66V, Temperature: 24.87°C
ADC Value: 2049, Voltage: 1.65V, Temperature: 24.99°C
ADC Value: 2046, Voltage: 1.65V, Temperature: 25.09°C
...
数据处理与温度换算需同步进行 通过 CH32V17 的标准库函数可快速配置 ADC 为连续转换模式 代码示例很实用,可以直接拿来用在类似的项目中
出个多ADC外设的,这样就可以实现同步采样了。
页:
[1]