CH32V307 教程 [第五集] [ ADC ]
目录
任务目标
使用 ADC 采样模拟信号,生成随机值。
基础知识
通过 资料 -> openCH 赤菟开发板开发资源 v1.0.0
中的引脚定义可以看到 PA1 被引出,并可作为 ADC 采样口。
通过 资料 -> CH32FV2x_V3xRM
中 ADC 章节发现:ADC 的最高输入时钟为 14 MHz。
可以通过输入时钟频率算出 ADC 总采样时间与采样率。
实现过程
分析下任务目标,其实在我们的代码中只需要三个部分:
初始化 ADC 函数。
获取 ADC 采样值的函数。
main() 函数。
明确好目标直接去官方例程 CH32V307EVT 的 EXAM -> ADC
中寻找可借鉴的例程。Internal_Temperature
是一个使用模块内部温度传感器的例程,其 main.c
中的 ADC_Function_Init()
函数 Get_ADC_Val()
函数符合预期。
创建新工程,修改晶振启动超时阈值 即 HSE_STARTUP_TIMEOUT, 将 main.c
中的代码删除,从 Internal_Temperature
例程的 main.c
中复制过来 ADC_Function_Init()
,Get_ADC_Val()
和 main()
这三个函数.
修改ADC_Function_Init()
函数。
原函数:
s16 Calibrattion_Val = 0;
/*********************************************************************
* @fn ADC_Function_Init
*
* @brief Initializes ADC collection.
*
* @return none
*/
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 = DISABLE;
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_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
ADC_TempSensorVrefintCmd(ENABLE);
}
发现只需修改 GPIO 的引脚序号,并删除有关温度传感器的相关内容。
修改后的 ADC 初始化函数如下:
/* Global Variable */
s16 Calibrattion_Val = 0;//校准值
/********************************************************************
* 函 数 名 : ADC1_Init
* 函数功能 : 初始化ADC
* 输 入 : 无
* 输 出 : 无
********************************************************************/
void ADC1_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //初始化ADC时钟,设置时钟为PCLK2的8分频,最大时钟为14MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure); //配置PA1口为AD输入口
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //设置AD模式为单独模式,只使用ADC1
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_Cmd(ADC1, ENABLE); //使能ADC
ADC_BufferCmd(ADC1, DISABLE); //disable buffer
//测量ADC校准数据,也可以不使用。
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
}
Get_ADC_Val()
函数不需修改,可以直接使用:
/********************************************************************
* 函 数 名 : Get_ADC_Val()
* 函数功能 : 返回ADCx转换出的结果数据。
* 输 入 : 通道数
* 输 出 : ADCx转换出的结果数据
********************************************************************/
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;
}
修改 main()
函数。
原函数:
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
u16 ADC_val;
s32 val_mv;
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
ADC_Function_Init();
printf("CalibrattionValue:%d\n", Calibrattion_Val);
while(1)
{
ADC_val = Get_ADC_Average( ADC_Channel_TempSensor, 10 );
Delay_Ms(500);
ADC_val = Get_ConversionVal(ADC_val+Calibrattion_Val);
printf( "ADC-Val:%04d\r\n", ADC_val);
val_mv = (ADC_val*3300/4096);
printf("mv-T-%d,%0d\n",val_mv ,TempSensor_Volt_To_Temper(val_mv));
Delay_Ms(2);
}
}
修改为只需输出校准值和 ADC 对 PA1 采样的数据。
修改后:
/********************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
int main(void)
{
u16 i;
Delay_Init();
USART_Printf_Init(115200);
ADC1_Init();
printf("CalibrattionValue:%d\n", Calibrattion_Val); //串口打印出校准值
while(1)
{
i=Get_ADC_Val(1);
printf("%d\r\n", i); //返回ADC转换结果。
Delay_Ms(100);
}
}
实验代码
/**
******************************************************************
* @file main.c
* @author xy,Benue
* @version V1.0
* @date 2022-1-19
* @brief 使用 ADC 外设产生随机值。
******************************************************************
* @attention
* VeriMake 用于CH32V307例程
******************************************************************
*/
#include "debug.h"// 包含 CH32V307 的头文件,C 标准单元库和delay()函数
/* Global Variable */
s16 Calibrattion_Val = 0;//校准值
/********************************************************************
* 函 数 名 : ADC1_Init
* 函数功能 : 初始化ADC
* 输 入 : 无
* 输 出 : 无
********************************************************************/
void ADC1_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //初始化ADC时钟,设置时钟为PCLK2的8分频,最大时钟为14MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure); //配置PA1口为AD输入口
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //设置AD模式为单独模式,只使用ADC1
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_Cmd(ADC1, ENABLE); //使能ADC
ADC_BufferCmd(ADC1, DISABLE); //disable buffer
//测量ADC校准数据,也可以不使用。
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
}
/********************************************************************
* 函 数 名 : Get_ADC_Val()
* 函数功能 : 返回ADCx转换出的结果数据。
* 输 入 : 通道数
* 输 出 : ADCx转换出的结果数据
********************************************************************/
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;
}
/********************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
int main(void)
{
u16 i;
Delay_Init();
USART_Printf_Init(115200);
ADC1_Init();
printf("CalibrattionValue:%d\n", Calibrattion_Val); //串口打印出校准值
while(1)
{
i=Get_ADC_Val(1);
printf("%d\r\n", i); //返回ADC转换结果。
Delay_Ms(100);
}
}
实验现象
串口定时输出 ADC 从 PA1 采样的模拟值,用手指触摸即可让它变化,最后几位即可作为随机数使用。