温馨提示:本文不会重复之前提到的内容,如需查看,请参考附录
目录
重点提炼:
数据读取:
双ADC同步转换方式中低16位是ADC1的数据,高16位是ADC2 的数据。需要做如下处理:
uint32_t adcValue=dmaDataBuffer[0]; //dmaDataBuffer是DMA读取数据的缓冲区
uint32_t ADC1_val=adcValue&0x0000FFFF;
uint32_t ADC2_val=(adcValue&0xFFFF0000)>>16;
理论知识:
一、需求分析
本篇文章是上一篇文章四、各种外设驱动(十一)ADC(1):软件触发与中断触发方式的进阶,使用双ADC同步转换功能。
开发板上有两个电压采集R37和R38,如图:
查看原理图可知,R37连接PB15,R38连接PB12.而使用CubeMX可以查到:PB15是ADC2的通道15,PB12是ADC1的通道11。
我们将使用ADC1和ADC2同步采集两个通道的信号,分别采集对应的“电压采集”的信号。
1、需要的外设资源分析:
- ADC1_IN11——PB12——R38(电压采集2)
- ADC2_IN15——PB15——R37(电压采集1)
- LCD
- DMA
2、外设具体分析:
查看原理图,和手册:
与LCD的相关的部分请参考附录。
比赛时ADC可能需要配置的部分:
- ADC Mode:选择需要的通道,选择single_ended模式
- ADCs_Common_Settings:独立模式或多个ADC合作的模式
- DMA Access Mode(DMA接入方式)
- Delay between 2 sampling phases:(可选)(2个采样相位之间的间隔)
- Resolution:转换精度
- DMA Continuous Requests:是否连续产生DMA请求
- External Trigger Conversion Source:中断触发方式
- External Trigger Conversion Edge:外部触发时使用的信号沿
- Rank里的Sampling Time:(可选)采样时间,不设置也行,比赛时为提高准确性设置为640.5。
二、软件配置
参考附录的内容,建立名为“ ADC_DualSimu_Demo”的项目。
按照分析配置外设:
先打开ADC2_IN15和ADC1_IN11,选择single_ended。
在ADC1_IN11的配置中
- ADCs_Common_Settings设置为Dual regular simultaneous mode only(双规则同步转换模式)
- DMA Access Mode(DMA接入方式)设置为DMA access mode enable
- Delay between 2 sampling phases(2个采样相位之间的间隔),保持默认或根据要求。
- External Trigger Conversion Source:设置为Timer 6 Trigger Out event,使用定时器TIM6产生的UEV中断触发ADC转换
结果如图所示:
DMA Settings:
ADC1只有一个DMA请求,为这个DMA请求配置DMA 通道(任意通道即可),设置DMA 传输属性参数,设置界面如下图所示。
在DMA Request Settings组中将Mode(工作模式)设置为Circular(循环模式),将外设和存储器的数据宽度都设置为Word——因为ADC转换结果数据寄存器是32位的。
ADC2一般不用再做设置,但要注意:
ADC2除了Rank通道号外的其他可配置参数参赛都要和ADC1一致,以保证两个ADC能同步采集。
TIM6:
按照附录中内容将TIM6设置为500ms一次触发Trigger Event Selection中断设置为Update Event(UEV)。
无需开启TIM6的中断。
生成项目文件后,打开MDK;
导入LCD驱动程序文件,编译。
三、程序功能实现
用到的函数:
中断服务函数:
void DMA1_Channel1_IRQHandler(void)
ADC函数:
HAL_ADCEx_MultiModeStart_DMA(&hadc1,dmaDataBuffer,DATA_LEN);
HAL_ADCEx_MultiModeStart_DMA(&hadc2,dmaDataBuffer,DATA_LEN);
程序流程:
- 在初始化HAL库后初始化LCD,之前实验发现中断可能会影响LCD初始化。
- 在main.c中定义一些全局变量,在main.h中引入这些变量。
- 以同步DMA方式启动ADC1和ADC2;启动定时器。
- 找到并实现DMA的中断服务函数。
在MDK中编写代码:
在 main.c 的对应代码段,定义全局变量;
/* USER CODE BEGIN PV */
uint32_t dmaDataBuffer[DATA_LEN];
/* USER CODE END PV */
在main.h中的对应代码段定义宏、引用外部变量。
/* USER CODE BEGIN Private defines */
#define DATA_LEN 1
extern uint32_t dmaDataBuffer[DATA_LEN];
/* USER CODE END Private defines */
在main.h中的对应代码段进行引入文件。
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include <stdio.h>
/* USER CODE END Includes */
在 main.c 的 /* USER CODE BEGIN WHILE */ 代码段,编写以下代码
/* USER CODE BEGIN WHILE */
HAL_ADCEx_MultiModeStart_DMA(&hadc1,dmaDataBuffer,DATA_LEN);
HAL_ADCEx_MultiModeStart_DMA(&hadc2,dmaDataBuffer,DATA_LEN);
HAL_TIM_Base_Start(&htim6);
while (1)
{
/* USER CODE END WHILE */
在找到ADC对应的DMA的中断服务函数:
(详细步骤请查看附录NVIC部分)
这里与上一篇文章有些不同,因为是对两个ADC使用同一个DMA通道,所以不用判断是谁引起中断,也就是说可以直接使用中断服务函数。
在代码段编写程序:
/* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
uint32_t Volt;
uint32_t adcValue=dmaDataBuffer[0];
char str[20];
uint32_t ADC1_val=adcValue&0x0000FFFF;
Volt = (3300*ADC1_val)>>12;
sprintf(str,"R38 Volt = %d",Volt);
LCD_DisplayStringLine(Line3,str);
uint32_t ADC2_val=(adcValue&0xFFFF0000)>>16;
Volt = (3300*ADC2_val)>>12;
sprintf(str,"R37 Volt = %d",Volt);
LCD_DisplayStringLine(Line5,str);
/* USER CODE END DMA1_Channel1_IRQn 1 */
四、运行测试
编译、下载(见附录)。
运行结果如下:
ADC_DualSimu_Demo