所用芯片:stm32f429 本文实现了(包含cubemx的配置) 1、利用PWM制作呼吸灯:定时器3通道4 (1.2) 2、定时器捕获按键的高电平时间:定时器5通道1(3.2) 3、定时器捕获方波的周期和占空比:定时器5通道1(4.2) 参考: 正点原子:STM32F429开发指南-HAL库版本_V1.1 自动重装载值
1、PWM制作呼吸灯
1.1、cubemx的设置
- pwm:
脉冲宽度调制,是英文“Pulse Width Modulation” 的缩写,简称脉宽调制 - 占空比:
在一个脉冲周期,通电时间相对于总时间所占的比例 占空比 = Pulse(脉冲时长)/Counter Period(重装载值) - 脉冲周期
T= (89+1)( 499+1))/90Mhz=500us 90Mhz是定时器对应的时钟频率
1.2、keil设置
其他代码用cubemx自动生成的就行
uint8_t chang = 0;
uint32_t pulse = 0;
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);
while (1)
{
if(change == 0)
{
pulse++;
if(pulse >= 300)
{
change = 1;
}
}
else
{
pulse--;
if(pulse <= 1)
{
change = 0;
}
}
HAL_Delay(10);
TIM3->CCR4 = pulse;
}
2、上升沿捕获
2.1、控制寄存器
TIMx_CR1 控制寄存器 1 TIMx control register 1
- 位 9:8 CKD:
时钟分频 (Clock division) 此位域指示定时器时钟 (CK_INT) 频率与数字滤波器所使用的采样时钟( ETR、TIx)之间的 分频比, 00: tDTS = tCK_INT 01: tDTS = 2 × tCK_INT 10: tDTS = 4 × tCK_INT 11:保留 - 位 7 ARPE:
自动重载预装载使能 (Auto-reload preload enable) 0: TIMx_ARR 寄存器不进行缓冲 1: TIMx_ARR 寄存器进行缓冲
TIMx 控制寄存器 2 (TIMx_CR2)
- 位 6:4 MMS:主模式选择
(Master mode selection) 这些位可选择主模式下将要发送到从定时器以实现同步的信息 (TRGO)。这些位的组合如下: 000: 复位––TIMx_EGR 寄存器中的 UG 位用作触发输出 (TRGO)。如果复位由触发输入 生成(从模式控制器配置为复位模式),则 TRGO 上的信号相比实际复位会有延迟。 001: 使能––计数器使能信号 (CNT_EN) 用作触发输出 (TRGO)。该触发输出可用于同时 启动多个定时器,或者控制在一段时间内使能从定时器。计数器使能信号可由CEN控制位产 生。当配置为门控模式时,也可由触发输入产生。 当计数器使能信号由触发输入控制时, TRGO 上会存在延迟,选择主/从模式时除外(请参见 TIMx_SMCR 寄存器中 MSM 位的说明)。 010: 更新––选择更新事件作为触发输出 (TRGO)。例如,主定时器可用作从定时器的预分 频器。 011: 比较脉冲––一旦发生输入捕获或比较匹配事件,当 CC1IF 被置 1 时(即使已为高电 平),触发输出都会发送一个正脉冲 (TRGO)。 (TRGO)? 100: 比较––OC1REF 信号用作触发输出 (TRGO)? 101: 比较––OC2REF 信号用作触发输出 (TRGO)? 110: 比较––OC3REF 信号用作触发输出 (TRGO)? 111: 比较––OC4REF 信号用作触发输出 (TRGO)
2.2、事件生成寄存器
TIMx event generation register
- 位 0 UG:
更新生成 (Update generation) 该位可通过软件置 1,并由硬件自动清零。 0:不执行任何操作 1:重新初始化计数器并生成寄存器更新事件。 请注意,预分频器计数器也将清零(但预分频比不受影响)。如果选择中心对齐模式或 DIR=0(递增计数),计数器将清零;如果 DIR=1(递减计数),计数器将使用自动重载值 (TIMx_ARR)。 - 计数器使能信号 (CNT_EN)
2.3、从模式控制寄存器
TIMx_SMCR TIMx slave mode control register
- 位 7 MSM:
主/从模式 (Master/Slave mode) 0:不执行任何操作 1:当前定时器的触发输入事件( TRGI)的动作被推迟,以使当前定时器与其从定时器实现 完美同步(通过 TRGO)。此设置适用于单个外部事件对多个定时器进行同步的情况。
2.4、CubeMx设置
其他的看上面的寄存器
2.5、捕获上升沿
当检测到PA0上升沿就触发中断 定时器5
用cubemx生成代码后 KEY_UP的初始化在HAL_TIM_Base_MspInit里面
HAL_TIM_Base_Start_IT(&htim5);
HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_1);
重写捕获中断函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
}
当按下key_up bs0会被点亮
3、捕获高电平时间
cubemx的配置和2.5一样需要重写两个中断函数
3.1、位运算
GPIOA->ODR &= 0XFF0F;
GPIOA->ODR |= 1 << 5;
GPIOA->ODR &= (uint16_t)~(1<<3);
3.2、思路
- 计数器
TIM5 每刷新一个周期 计数器 TIM5_CNT就+1 如:TIM5的频率为 1Mhz 则每过1us TIM5_CNT++; - 大概的流程
3.3、代码
下面是正点原子官方的例程,和我上面的逻辑有点出入,但实际执行效果差不多 注释我改了一部分,方便自己理解,但捕获高电平其实不用这么麻烦
uint16_t tim5_ch1_cap_sta = 0;
uint32_t tim5_ch1_cap_val;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
if((tim5_ch1_cap_sta&0x8000) == 0)
{
if((tim5_ch1_cap_sta&0x4000))
{
tim5_ch1_cap_sta |= 0x8000;
tim5_ch1_cap_val=HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
}
else
{
__HAL_TIM_DISABLE(&htim5);
__HAL_TIM_SET_COUNTER(&htim5,0);
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
__HAL_TIM_ENABLE(&htim5);
tim5_ch1_cap_sta = 0;
tim5_ch1_cap_val = 0;
tim5_ch1_cap_sta |= 0x4000;
}
}
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
if((tim5_ch1_cap_sta&0x8000) == 0)
{
if((tim5_ch1_cap_sta&0x4000))
{
if((tim5_ch1_cap_sta&0x3fff) == 0x3fff)
{
tim5_ch1_cap_sta |= 0x8000;
tim5_ch1_cap_val &= 0xffffffff;
}
else
{
tim5_ch1_cap_sta++;
}
}
}
}
}
long long temp;
while (1)
{
if((tim5_ch1_cap_sta&0x8000))
{
u1_printf("Capture ok\r\n");
temp = tim5_ch1_cap_sta&0x3fff;
temp = temp*65535;
temp = temp + tim5_ch1_cap_val;
u1_printf("%.2lf s\r\n",(double)temp/(1000*1000));
HAL_Delay(1000);
break;
}
}
u1_printf("done\r\n");
按住PA0几秒钟再松开 有个0.1s左右的误差,拿手机测的 定时器5用的这个时钟,只需要把预分频(PSC)设置为89就可以实现1us 计数器+1
3.4、关于自动重装载值
自动重载寄存器 ARR 用来存放与计数器 CNT 比较的值,如果两个值相等, 对于高级定时器,就递减重复计数器,当重复计数器减为零时就产生更新或中断。 如果没有使用到重复计数器时,就直接产生更新和中断。 对于基本定时器和通用定时器,也就产生更新和中断。
Counter Period的值也就是计数器 CNT 的值 这个后面还有个括号中(AutoReload Register …),其实也就是设置自动重载寄存器的值 ARR。 下面还有个auto-reload preload (自动重装载预装载),一般就直接Disable 都装载好了,而且一般也不轻易改变这个值,所以我也用不到auto-reload preload。
貌似改变自动重装载值不会影响2.6的结果
4、捕获方波的周期,占空比
只是自己写着玩,并没有真的拿方波测试过
4.1、思路
4.2、代码
配置那些和2.4一样 依旧定时器5 通道1 主要是两个中断处理函数中写逻辑
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
具体的过程注释写得很详细
uint16_t tim5_ch1_cap_sta = 0;
uint32_t tim5_ch1_cap_val;
uint32_t tim5_ch1_cap_hoverflow;
uint32_t tim5_ch1_cap_hval = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
if(tim5_ch1_cap_sta&0x4000)
{
tim5_ch1_cap_sta &= ~0x4000;
tim5_ch1_cap_hoverflow = (tim5_ch1_cap_sta&0xfff);
tim5_ch1_cap_hval=HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
tim5_ch1_cap_sta |= 0x8000;
}
else if(tim5_ch1_cap_sta&0x8000)
{
tim5_ch1_cap_sta &= ~0x8000;
tim5_ch1_cap_val=HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);
tim5_ch1_cap_sta|= 0x2000;
}
else if(tim5_ch1_cap_sta&0x1000)
{
tim5_ch1_cap_sta = 0;
tim5_ch1_cap_val = 0;
tim5_ch1_cap_hval = 0;
__HAL_TIM_DISABLE(&htim5);
__HAL_TIM_SET_COUNTER(&htim5,0);
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
__HAL_TIM_ENABLE(&htim5);
tim5_ch1_cap_sta |= 0x4000;
}
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
if((tim5_ch1_cap_sta&0x2000) == 0)
{
if((tim5_ch1_cap_sta&0xfff) == 0xfff)
{
tim5_ch1_cap_sta &= 0x2000;
tim5_ch1_cap_val &= 0xffffffff;
}
else
{
tim5_ch1_cap_sta++;
}
}
}
}
long long period;
long long duty_ratio;
tim5_ch1_cap_sta |= 0x1000;
while (1)
{
if(tim5_ch1_cap_sta&0x2000)
{
u1_printf("capture ok\r\n");
period =tim5_ch1_cap_sta&0xfff;
period = period*65535;
period = period + tim5_ch1_cap_val;
u1_printf("周期:%.2lf s\r\n",((double)(period/1000))/1000);
duty_ratio = tim5_ch1_cap_hoverflow&0xfff;
duty_ratio = duty_ratio*65535;
duty_ratio = duty_ratio + tim5_ch1_cap_hval;
u1_printf("占空比:%.2lf %%\r\n",(double)(100.0*duty_ratio/period));
break;
}
}
理论上这个可以捕获小于1Mhz的方波,我按按键来模拟方波测试没问题
|