最近在学习和电源相关的东西,之前一直接触的是和控制相关的单片机知识,用也只是用相关的外设,所以做一些记录,对于一些功能还是要熟悉并且记录一下。
定时器的补充介绍
由此图可以看出将时钟输入进触发控制器后,有两种输出方式:TRGO与PWM
TRGO
触发信号:
在嵌入式系统、微控制器(如STM32)以及许多其他电子系统中,触发信号(Trigger Signal)通常用于启动某个事件、中断、操作或转换。这些信号可以是内部的,由微控制器自身产生,也可以是外部的,由其他硬件组件(如传感器、开关或其他微控制器)提供。
触发输出TRGO介绍:
触发输出TRGO是定时器模块中的一个输出信号,当定时器满足特定条件时,这个信号会被激活并输出到微控制器的总线上。其他外设或定时器可以通过监听这个总线来接收TRGO信号,并根据需要执行相应的操作。
TRGO信号输出,此信号可以用来触发ADC,DAC和其他定时器等 在TIMx_CR2寄存器中有对其相关的描述:
TRGO 触发产生 ADC DMA的CUBEMX配置
使用的是ADC1_IN1
引脚PA1
ADC配置
Scan Conversion Mode---- Disabled (单通道)
Continuous Conversion Mode ---- Disabled (关闭 连续转换模式)
Discontinuous Conversion Mode ----Disabled (关闭 不连续转换模式)
DMA Continuous Requests ----Enabled (开启DMA连续请求)
External Trigger Conversion Source ---- Timer 2 Trigger Out event (选择外置触发转换源:这里是定时器2的触发输出事件)
DMA配置
DMA模式为 Circular
TIM2定时器配置
使能 自动重装载功能(auto-reload preload) Enable
触发事件(TRGO):选择定时器更新事件
定时器频率根据计算的是 84000000/4/840 = 25Khz
为什么要开启这两个呢?我的理解是:
定时器是工作在向上递增模式下的,cnt寄存器的值会从0递增到ARR(这里设置的是840-1)的值,使能自动重装载功能,每次递增到ARR的值后,会重新回到0 继续这一递增的过程;
而选择定时器更新事件的目的就是:当ARR计数值满了之后,定时器就会触发溢出这一事件,将其作为一个信号传递出去给ADC,作为ADC启动采集的信号。
ps.在adc那边有一个是
External Trigger Conversion Edge :Trigger detection on the rising edge选择的是上升沿触发。
既然是定时器计数器是如何有上升沿下降沿的呢?
定时器通常是一个计数器,用来按预定的时间间隔计数。在定时器向上计数或向下计数时,其输出信号(如定时器的更新事件、溢出事件、输出比较事件等)可能会在一定条件下表现出上升沿和下降沿的概念。
上升沿:在一些定时器配置中,计数器值从最低值(如0)增加到最大值时,可能会产生一个“上升沿”的触发事件。
下降沿:同样地,当计数器值从最大值(如65535)回绕到最小值时,也可以视为一种“下降沿”的触发事件。
这种上升沿和下降沿的变化是根据定时器的计数方向和触发事件的配置来理解的。例如,当定时器的计数值从0到最大值时,产生一个触发事件,可以理解为上升沿;从最大值回绕到0时,又产生另一个事件,可以理解为下降沿。
PWM 触发产生 ADC DMA的CUBEMX配置
基本没啥要修改的
这里选用TIM2_Channel2作为触发源
TIM2配置
通道二 PWM输出
关闭使能自动重装载 disable
触发信号改为: Output Compare (OC2REF)
PS:Pulse值记得改,把占空比调成50% 即这个值应该改成420-1(上图忘改了)
ADC配置
改成对应的触发方式就行了
代码实现:
/* USER CODE BEGIN 0 */
uint16_t ADC_Value[4];
uint8_t AdcConvEnd=0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc==&hadc1)
{
AdcConvEnd=1;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
delay_init(168);
OLED_Init(); //初始化OLED
HAL_TIM_Base_Start(&htim2);//开启定时器2 TRGO开启这句话
HAL_TIM_PWM_Start (&htim2,TIM_CHANNEL_2);//PWM开启这句话
/* USER CODE END 2 */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//printf("dd\r\n");
if(AdcConvEnd)
{
uint16_t averageValue = 0;
for(int i = 0; i < 4; i++)
{
averageValue += ADC_Value[i];
// printf("ADC_Value[%d]:%.3f\r\n",i,(float)ADC_Value[i]*3.3f/4096);
OLED_Showdecimal(0,0,(float)ADC_Value[i]*3.3f/4096,1,3,12,0);
OLED_Refresh();
}
AdcConvEnd=0;//清楚标志变量
averageValue/=4;
// printf("%d\r\n",averageValue);
OLED_ShowNum(0,10,averageValue,4,12);
OLED_Refresh();
}
}
/* USER CODE END 3 */
}
效果图
多通道采集
在这里,我们使用两路ADC 一个采3.3v 一个采地
ADC配置:
在基于单通道dma采集的基础上更改即可。
由于存放数据的数组是4位的(ADC_VALUE[4])
所以第0,2位存放的就是ADC_IN1的数据,1,3位存放的就是ADC_IN2的数据。
示例代码(其实就是在adc中断中打印四个位的值)
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//printf("dd\r\n");
if(AdcConvEnd)
{
printf("%d %d %d %d\r\n",ADC_Value[0],ADC_Value[1],ADC_Value[2],ADC_Value[3]);
AdcConvEnd=0;
}