目录
一、ADC规则组多通道输入
当ADC只有一个输入通道,在转换结束后可以及时读出结果数据寄存器的内容。
当规则转换组有多个通道时,应该使用扫描转换模式(Scan Conversion Mode),ADC在转换完一个通道后立刻转换下一个通道,直到规则组内的通道序列全部转换完成。规则转换只有一个转换结果数据寄存器,虽然可以设置在每个通道转换完之后就产生EOC事件中断,但是在多通道情况下,在EOC事件中断里读取转换结果数据可能是来不及的,更谈不上对数据进行显示或处理。
如果规则转换组有多个输入通道,应该使用DMA,使转换结果数据通过DMA传输自动保存到缓冲区中,在一个规则组转换结束后再对数据进行处理,或者采集多次数据后再处理。
本例中建立一个工程,为规则组设置3个输入通道,使用扫描转换模式,通过DMA方式传输ADC转换结果数据。参考本文作者的文章:细说STM32单片机用定时器中断启动ADC转换并通过串口发送数据的方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/144027184
二、工程配置
1、 时钟、DEBUG
同参考文章。
2、 ADC1
(1) ADCs_Common_Settings组
- ADC1_IN1,single_ended;
- ADC1_IN2,single_ended;
-
选中Vbat Channel;
在ADC1的模式设置中,选择3个输入通道,IN1(PA0)、IN2(PA1)是外部模拟量输入通道,接可调电阻产生的可变电压。IN1、IN2在开发板的底板(关于底板请参考作者的其他文章中的描述,需要底板或者设计资料,请给作者留言)上调节。另外的IN3(PA2)使用内部输入通道,Vrefint Channel是内部参考电压通道,Vbat Channel是备用电源电压通道,二者择一。
(2)ADC_Settings组
- Clock Prescaler:选择同步时钟4分频;
- Resolution:12位;
- Data Alignment:右对齐(Right alignment);
- Scan Conversion Mode:Enabled;
- End of Conversion Selection:end of single conversion;
- Continuous Conversion Mode:Enabled;
- Discontinuous Conversion Mode:Disabled;
- DMA Continuous Requests:Enabled;
开启扫描转换模式(Scan Conversion Mode)和DMA连续请求(DMA Continuous Requests)。
(3)ADC_Regular_ConversionMode
- Number of Conversion:3;
- External Trigger Conversion Source:Timer 3 Trigger out envent;用于设置启动ADC转换的外部触发信号源,列表中列出了所有可选的信号源,是一些定时器的Trigger Out event或Capture Compare event,这里选择Timer 3 Trigger Out event,也就是定时器TIM3的TRGO信号。
- External Trigger Conversion Edge:上跳沿;用于设置触发转换的跳变沿,可选上跳沿、下跳沿或双边都触发。这里选择上跳沿,因为TRGO是一个短时正脉冲信号。
- Rank1:Channel1,采样时间(Sampling Time)=24.5,无偏移;
- Rank2:Channel2,采样时间(Sampling Time)=24.5,无偏移;
- Rank3:Channel Vbat,采样时间(Sampling Time)=24.5,无偏移;
设置转换个数为3,下面会自动生成3个Rank的设置,分别设置每个Rank的输入通道和采样时间,每个通道的采样时间可以不一样。3个Rank里模拟通道出现的顺序就是规则组转换的顺序。
ADC_Settings组里的参数End of Conversion Selection的设定值不变,仍然是在每个通道转换完之后产生EOC信号。
其它参数默认。
3、USART2和Timer 3
同参考文章。
4、NVIC
开启DMA的全局中断。
5、DMA
ADC1只有一个DMA请求,为这个DMA请求配置DMA流DMA1 Channel1,设置DMA传输属性参数,DMA传输方向自动设置为Peripheral To Memory(外设到存储器)在DMA Request Settings组中将Mode(工作模式)设置为Circular(循环模式),将外设和存储器的数据宽度都设置为Word,因为ADC转换结果数据寄存器是32位的。存储器设置为地址自增加。
(1)DMA Settings
- 为ADC1选择DMA1 Channel1,外设到内存,优先级中等;
- Mode:Circular;
- Data Width:Word;
(2) 使用DMA时是否开启外设的全局中断
在使用ADC1的DMA方式传输时,即使不开启ADC1的全局中断,DMA传输功能也能正常工作,所以在NVIC设置部分关闭ADC1的全局中断。
外设使用DMA时是否需要开启外设的全局中断,不同的外设情况不一样。例如,UART使用DMA时就必须开启UART的全局中断,虽然可以禁止UART的两个主要中断事件源。在外设使用DMA时,建议尽量不开启外设的全局中断,若必须开启,也要禁止外设的主要事件源产生硬件中断,因为DMA的传输完成事件中断使用外设的回调函数,若开启外设的中断事件源,则可能导致一个事件发生时回调函数被调用两次。
三、软件设计
1、main.c
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
#define BATCH_DATA_LEN 3 // DMA data buffer length, must be an integer multiple of the number of channels
uint32_t dmaDataBuffer[BATCH_DATA_LEN]; // DMA data buffer
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc1, dmaDataBuffer, BATCH_DATA_LEN); // Start ADC1, DMA mode
HAL_TIM_Base_Start(&htim3); // Start TIM3
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
/* Callback function for DMA stream transfer completion event interrupt */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
uint32_t adcValue=0, Volt;
for(uint8_t i=0; i<BATCH_DATA_LEN;i++)
{
// The buffer contains the conversion results of 3 channels.
adcValue=dmaDataBuffer[i];
Volt=3300*adcValue;
Volt=Volt>>12;
printf("ADC[%d] = %ld\r\n",i,Volt );
}
}
//串口打印
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart2,(uint8_t *)&ch,1,0xFFFF);
return ch;
}
/* USER CODE END 4 */
其中,dmaDataBuffer是DMA传输数据的缓冲区,是元素类型为uint32_t的数组,该数组的长度为BATCH_DATA_LEN。程序中定义BATCH_DATA_LEN的值为3,所以,发生DMA传输完成事件中断时,数组dmaDataBuffer中就存储了3个通道的一次转换结果,在DMA传输完成事件中断的回调函数里,就可以读出3个通道的转换结果。因为设置DMA工作模式为循环模式,所以会在定时器TIM3驱动下一直进行ADC转换和数据传输。
DMA缓冲区长度BATCH_DATA_LEN也可以设置为3的整数倍,例如,设置为30。那么发生DMA传输完成中断时,数组dmaDataBuffer就存储了3个通道10个采样点的数据。在实际的ADC数据采集中,一般是采集一定的数据点之后再处理、显示或存储,还可以使用双缓冲区进一步提高效率。
通过跟踪分析函数HAL_ADC_Start_DMA()和DMA流中断处理函数HAL_DMA_IRQHandler()的源代码,发现DMA流中断事件与ADC回调函数之间的关系见参考文章。DMA流的传输完成中断事件(DMA_IT_TC)关联着ADC的回调函数HAL_ADC_ConvCpltCallback()。
四、下载运行
同样的参数设置和串口设置,DMA传输ADC的转换值到缓存,传输效率很高。
串口助手上不断刷新显示3个通道的采集值。调节开发板上的电位器,可以看到Channel1、2的电压值明显变化;内部参考电压通道接的是内部1.2V调压器的输出,所以测量值是1200mV左右;VBAT通道内部有桥接分压器,实际测量的电压是VBAT/2。