STM32-HAL库配置定时器溢出中断实现ADC DMA采样
目录
1.配置ADC+DMA
2.配置定时器
3.代码实践
4.实践
1.配置ADC+DMA
我们配置ADC+DMA是为了ADC采样完成之后通过DMA来传输避免占用CPU。贴出配置图片:
需要注意的是,我此次只配置了一个通道,如果要用多个通道,有些配置是需要更改的。
在DMA中需要将Mode配置成Circular。接下来回到Parameter Settings中将Scan Conversion Mode需要配置成Enable,若想要实现自己控制采样的话,需要将Cotinues Coversion Mode成Disable,将Discotinuous Coversion Mode改成Disable,DMA Continuous Requests配置成Enable。我配置的是完成所有通道转换完成之后才能有中断触发,将End of Coversion Selection配置成Eoc flag at the end of all conversions。
多个通道话,需要将Number of Conversion增加到自己需要采样的通道数,同时将Channel中改变通道。Sampling Time需要根据采样结果来更改,为了方便我将其设置成3 Cycles。此时ADC的配置基本完成。
2.配置定时器
首先要计算好自己需要的频率,我这里为了测试方便,将Psc配置成180-1,将定时器1的时钟进行180分频,最后定时器1的时钟是1MHz。
解释一下如何计算中心对齐模式的频率:我们先回到Up模式(向上计算)计算频率的方法:f=定时器时钟频率/((ARR+1)X(PSC+1))=最终配置的频率。而在中心对齐模式我们也可以如此计算只是分母有点变化(因为中心对齐模式是先向上计算到ARR-1,然后再从ARR-1向下减),所以它的频率为Up或Down Mode模式下的一半即f=(定时器时钟频率/(ARR*(PSC+1)*2))。以我上图的配置来计算:f=(180MHz)/((500-1)*(180-1+1)*2)=1.004KHz。贴上示波器图片:
为了实现两次溢出中断,我将Counter Mode配置成Center Aligned mode3(上溢出和下溢出中断触发),下图有三种不同中心对齐模式的差距:
最后特别要注意的是Repetition Counter(RCR-8 bits value),如果此时将其配置成1,则只会触发一次,因为它要满足在一个PWM周期中上溢中断次数等于下溢中断次数。若是配置成0,处于中心对齐模式3,则会有两次中断触发,分别是上溢和下溢。
注意:我这里为了演示方便,只选取了一个通道,而且该通道为Channel1,各位要根据自己的实际需要来配置,目的是为了在示波器中看波形来对比。
接下来配置PWM Generation Channel 1:
Mode配置成PWM Mode2,Pulse一开始可设置为0,CH Polarity设置为高电平。在PWM2模式中先是无效电平然后是有效电平,而PWM1中先是有效电平然后是无效电平,两者的区别只是在于一开始是高电平还是低电平,而PWM2是一开始低电平而已。
接下来要配置NVIC Settings:
开启定时器的更新中断。
3.代码实践
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
uint16_t adc_buffer=0;
uint16_t ConvFinshed=0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance==ADC1)
{
ConvFinshed=1;
HAL_GPIO_TogglePin(TEST_GPIO_Port,TEST_Pin);
}
}
/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == htim1.Instance)
{
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&adc_buffer,sizeof(adc_buffer)/sizeof(uint16_t));
}
}
/* USER CODE END 1 */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
HAL_TIM_PWM_Start_IT(&htim1,TIM_IT_UPDATE);
HAL_TIM_Base_Start_IT(&htim1);//开启定时器1
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,100);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
接下来解释一下代码:
在进入main函数之前需要添加以下代码:
uint16_t adc_buffer=0;
uint16_t ConvFinshed=0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance==ADC1)
{
ConvFinshed=1;
HAL_GPIO_TogglePin(TEST_GPIO_Port,TEST_Pin);
}
}
该函数是ADC转换完成回调函数,我们在里面设置一个输出电平反转为方便在示波器中看波形变化。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == htim1.Instance)
{
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&adc_buffer,sizeof(adc_buffer)/sizeof(uint16_t));
}
}
该函数是定时器1溢出中断回调函数,接下来我们这里开启开启ADC的DMA传输模式。因为我们之前配置是手动触发,所以是每一次触发时ADC才开始采样。
在int main(void)中需要添加以下代码:
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);//开启定时器1通道1输出PWM
HAL_TIM_PWM_Start_IT(&htim1,TIM_IT_UPDATE);//开启定时器的更新中断
HAL_TIM_Base_Start_IT(&htim1);//开启定时器1中断
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,100);//设置占空比。(500-100)/500=80%
4.实践
由于是鼠标画的,所以直线不太直
5.补充
第二日本人尝试将定时器设置中的Repetition Counter(RCR)设置为0,并尝试不同的中心对齐模式,发现只要将RCR设置为0,不论是哪种中心对齐模式似乎都能触发两个中断,贴上示波器图和程序图。
以上是设置为中心对齐模式1,RCR设置为0,发现向下计数的时候触发一次,向上计数触发一次。
以上是中心对齐模式2,RCR设置为0,一样是触发两次。
以上为中心对齐模式3,RCR设置为0,触发两次
以上是设置RCR为1时,不论是哪种中心对齐模式,都只会触发一次。
6.总结
若想在一个PWM周期中触发两次中断,无论设置哪个中心对齐模式,设置RCR为0都会触发两次中断。若想在一个PWM周期中只触发一次中断,无论设置哪个中心对齐模式,设置RCR为1都只会触发一次。
注意:本人是在PWM2模式下测试的。
作者:悠悠子衿12138