芯源半导体CW32
直播中

hehung

8年用户 659经验值
擅长:嵌入式技术
私信 关注

【CW32饭盒派开发板试用体验】4. ADC采样摇杆模块X轴和Y轴输出

【CW32饭盒派开发板试用体验】1. 开箱啦

【CW32饭盒派开发板试用体验】2. 使用扩展板串口UART3以及scanf重定向到串口

【CW32饭盒派开发板试用体验】3. 使用BTIM实现任务调度器

前言

本文主要讲解如何使用ADC,板载了一个电位计连接到了PB00上,官方提供了示例工程,本文就不做过多说明了。

本文使用ADC采集摇杆模块的模拟量,摇杆的X轴与Y轴分别连接到了PA0和PA1。

1 硬件连接

见下图:

  • PA0 - ADC_IN0(摇杆X轴)
  • PA1 - ADC_IN1(摇杆Y轴)
    1685538649330.png

知道了硬件连接,本来想参考官方示例工程直接上手的,但是看了下源代码,发现只能对一个通道进行单次转换,what???我想着这与我所知道的其他ADC不一样的呀,不应该是ADC下面的很多通道都可以单独转换吗?

然后看了下用户手册,发现转换模式有很多,如下,示例工程使用的就是单通道转换模式,只不过官方库文件封装的不是很好,导致使用起来产生了歧义。

建议官方可以再优化一下库,目前这个ADC的库用起来确实不是很好用。

看到有连续工作模式,但是只能支持4个通道连续采样,我只使用两个通道,所以最终还是确定使用单通道转换,只不过每转换完一个通道再切换到另一个通道就可以了,稍微麻烦一点。
1685624843584.png

下面是ADC支持的通道以及对应的引脚,CW32F030只有一路ADC,但是支持12个通道,如下:

1685624720199.png

单通道转换的步骤如下,用户手册写的还是比较清晰,这点好评。
1685624817389.png

2 软件实现

知道了硬件工作原理,接下来就是软件开发了,我使用的中断模式来采集ADC,因为使用polling模式连续采样两个通道的时候需要软件参与,会导致代码存在一些等待延时,效率底下,使用中断模式可以避免这点。

下面是adc相关代码:

  • Adc_Running: ADC采样启动函数,调用该函数会出发一次ADC转换,并将转换结果用printf打印,将该函数放置到100ms任务中运行
  • Adc_Init:初始化为单通道采集,并使中断,设置首先采样AN0,但是不会触发中断,触发中断用Adc_Running
  • Adc_InterruptHandler:中断处理函数,当中断第一次进入的时候,表示AN0采样结果完成,将结果写入adc_result[0]中,然后选择AN1,触发转换,当AN1转换完成,进入中断,将结果写入adc_result[1]中,然后选择AN0,但是不触发,再次等待Adc_Running来触发转换。
/*
@hehung
2023-5-28
email: 1398660197@qq.com
wechat: hehung95
reproduced and please indicate the source @hehung
*/

#include "app_common.h"
#include "app_adc.h"


static ADC_SingleChTypeDef ADC_SingleInitStruct;
static uint16_t adc_result[2] = {0, 0};
static uint8_t adc_int_cnt = 0;


void Adc_Running(void)
{
	ADC_SoftwareStartConvCmd(ENABLE);

	printf ("%d, %d\r\n", adc_result[0], adc_result[1]); 
}


void Adc_Init(void)
{
	__RCC_ADC_CLK_ENABLE();    // ADC时钟使能
	__RCC_GPIOA_CLK_ENABLE();

	//配置ADC测试IO口 
	PA00_ANALOG_ENABLE();
	PA01_ANALOG_ENABLE();

	//配置ADC测试IO口 
	ADC_SingleInitStruct.ADC_DiscardEn = ADC_DiscardNull;
	ADC_SingleInitStruct.ADC_InitStruct.ADC_AccEn = ADC_AccDisable;   //转换结果累加不使能
	ADC_SingleInitStruct.ADC_InitStruct.ADC_Align = ADC_AlignRight;   //ADC转换结果右对齐
	ADC_SingleInitStruct.ADC_InitStruct.ADC_ClkDiv = ADC_Clk_Div16;   //PCLK  
	ADC_SingleInitStruct.ADC_InitStruct.ADC_DMAEn = ADC_DmaDisable;   //关闭DMA传输
	ADC_SingleInitStruct.ADC_InitStruct.ADC_InBufEn = ADC_BufEnable;  //开启跟随器
	ADC_SingleInitStruct.ADC_InitStruct.ADC_OpMode = ADC_SingleChOneMode;   
	ADC_SingleInitStruct.ADC_InitStruct.ADC_SampleTime = ADC_SampTime5Clk; //5个ADC时钟周期
	ADC_SingleInitStruct.ADC_InitStruct.ADC_TsEn = ADC_TsDisable;    //内置温度传感器禁用
	ADC_SingleInitStruct.ADC_InitStruct.ADC_VrefSel = ADC_Vref_VDDA;//VDDA参考电压
	ADC_SingleInitStruct.ADC_WdtStruct.ADC_WdtAll = ADC_WdtDisable;  

	ADC_SingleInitStruct.ADC_Chmux = ADC_ExInputCH0;  
	ADC_SingleChOneModeCfg(&ADC_SingleInitStruct);

	ADC_ITConfig(ADC_IT_EOC, ENABLE);
    //ADC_ITConfig(ADC_IT_EOC | ADC_IT_EOS | ADC_IT_EOA, ENABLE);
    ADC_EnableIrq(ADC_INT_PRIORITY);
    ADC_ClearITPendingAll();


	ADC_Enable();    // 使能ADC

//	ADC_SoftwareStartConvCmd(ENABLE);
}

void Adc_InterruptHandler(void)
{
	if (ADC_GetITStatus(ADC_IT_EOC))
	{
		ADC_ClearITPendingBit(ADC_IT_EOC);

		adc_result[adc_int_cnt] = ADC_GetConversionValue();

		if (adc_int_cnt == 0)
		{
			ADC_SingleInitStruct.ADC_Chmux = ADC_ExInputCH1;  
			ADC_SingleChOneModeCfg(&ADC_SingleInitStruct);
			ADC_SoftwareStartConvCmd(ENABLE);
	
			adc_int_cnt++;
		}
		else
		{
			adc_int_cnt = 0;
			ADC_SingleInitStruct.ADC_Chmux = ADC_ExInputCH0;  
			ADC_SingleChOneModeCfg(&ADC_SingleInitStruct);
		}
	}
}

注:Adc_InterruptHandler函数需要放置到 interrupts_cw32f030.c的ADC_IRQHandler里面,如下:

void ADC_IRQHandler(void)
{
	extern void Adc_InterruptHandler(void);
  /* USER CODE BEGIN */
	Adc_InterruptHandler();
  /* USER CODE END */
}

3 实验结果

如下,试验成功。

更多回帖

发帖
×
20
完善资料,
赚取积分