本帖最后由 孤独的单行者 于 2024-5-28 10:30 编辑
本章节主要是进行定时器的基本使用以及PWM的产生与控制,结合前面的按键以及LED的控制,再加上串口的打印功能,就可以实现,不连接其他外部器件进行定时器的使用和测试。
我们首先看一下定时器的基本使用,在使用基本定时功能时,我们通常需要进行定时器的初始化后,在定时器的中断中进行处理,我们现在通过现有的资源实现LED的非阻塞闪烁,一个按键对应一个LED,并通过串口打印状态。
这里我们还要了解一个点,也就是系统的时钟频率是多少,注意之前的测试可以不考虑,这里要计算定时器的时间则必须要考虑了,下面是本芯片的时钟树,板载的外部高速时钟是8Mhz:
我们找到system_apm32f4xx.C中有关时钟的配置,注意一点,才发现这里的配置真的很乱,我不知道对于官方的SDK为什么会这样配置,可能只是为了能达到最大时钟吧,原始配置如下:
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_B) * PLL_A */
咱们简单计算一下,外部时钟8Mhz,按照如上的计算,PLL_VCO=200M,USB OTG FS = 28.57 M,SYSCLK= 100M,为了后续的USB功能的测试(一般USB的时钟要求为48M),我们重新梳理一下时钟配置,最大时钟不得超过120Mhz,为了比较好计算,SYSCLK取96中,一种参数计算配置如下:
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_B) * PLL_A */
#define PLL_B 8
/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLL_D */
#define PLL_D 4
#define PLL_A 192
/* SYSCLK = PLL_VCO / PLL_C */
#define PLL_C 2
接下来继续进行定时器的初始化配置:
void APM_TMR1_Init(void)
{
TMR_BaseConfig_T TMR_BaseConfigStruct;
/* Enable TMR1 Periph Clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);
APM_HCLKFreq = RCM_ReadHCLKFreq();
/* 定时10ms */
APM_TMR1_DIV = APM_HCLKFreq/1000*10/1000-1;
/* Config TMR1 */
TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
TMR_BaseConfigStruct.division = APM_TMR1_DIV;
TMR_BaseConfigStruct.period = 999;
TMR_BaseConfigStruct.repetitionCounter = 0;
TMR_ConfigTimeBase(TMR1, &TMR_BaseConfigStruct);
/* Enable TMR1 Interrupt */
TMR_EnableInterrupt(TMR1, TMR_INT_UPDATE);
NVIC_EnableIRQRequest(TMR1_UP_TMR10_IRQn, 0, 0);
TMR_Enable(TMR1);
}
在中断处理中加入按键判断,KEY2控制灯的闪烁:
void TMR1_UP_TMR10_IRQHandler(void)
{
if(TMR_ReadIntFlag(TMR1, TMR_INT_UPDATE) == SET)
{
TMR_ClearIntFlag(TMR1, TMR_INT_UPDATE);
if(APM_EINT0_flag == 1)
{
TMR1_cnt++;
if(TMR1_cnt%50 == 0)
{
APM_LED3_Toggle;
APM_LED2_Toggle;
}
}
}
}
接下来是通过定时器的PWM功能实现呼吸灯的效果,LED对应的PE5和PE6的引脚功能如下:
可以看到对应的是定时器9的CH1和CH2,我们用的产生PWM的定时器为定时器9,使用的时钟依然是APB2.
初始化如下:
void APM_TMR9_Init(void)
{
TMR_BaseConfig_T TMR_BaseConfigStruct;
TMR_OCConfig_T OCcongigStruct;
GPIO_Config_T GPIO_ConfigStruct;
/* Enable TMR1 Periph Clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR9);
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);
/* TMR9_CH1 */
GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_5, GPIO_AF_TMR9);
/* TMR9_CH2 */
GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_6, GPIO_AF_TMR9);
// /* Config TMR2 GPIO for output PWM */
// GPIO_ConfigStruct.pin = GPIO_PIN_5|GPIO_PIN_6;
// GPIO_ConfigStruct.mode = GPIO_MODE_AF;
// GPIO_ConfigStruct.otype = GPIO_OTYPE_PP;
// GPIO_ConfigStruct.speed = GPIO_SPEED_100MHz;
// GPIO_Config(GPIOE, &GPIO_ConfigStruct);
APM_HCLKFreq = RCM_ReadHCLKFreq();
/* 频率200Hz */
APM_TMR1_DIV = APM_HCLKFreq/200/1000-1;
/* Config TMR1 */
TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
TMR_BaseConfigStruct.division = 999;
TMR_BaseConfigStruct.period = APM_TMR1_DIV;
TMR_ConfigTimeBase(TMR9, &TMR_BaseConfigStruct);
/* Configure channel1 */
TMR_ConfigOCStructInit(&OCcongigStruct);
OCcongigStruct.mode = TMR_OC_MODE_PWM1;
OCcongigStruct.outputState = TMR_OC_STATE_ENABLE;
OCcongigStruct.polarity = TMR_OC_POLARITY_HIGH;
OCcongigStruct.pulse = 500;
TMR_ConfigOC1(TMR9, &OCcongigStruct);
TMR_ConfigOC1Preload(TMR9, TMR_OC_PRELOAD_ENABLE);
/* Configure channel2 */
OCcongigStruct.pulse = 200;
TMR_ConfigOC2(TMR9, &OCcongigStruct);
TMR_ConfigOC2Preload(TMR9, TMR_OC_PRELOAD_ENABLE);
TMR_EnableAutoReload(TMR9);
// TMR_Enable(TMR9);
}
注意,我们在初始化里面没有开启定时器和引脚的初始化,这里主要因为LED的引脚的功能是复用的,在正常状态默认是输出控制,只有开启后才是PWM控制,这里面的切换在按键中断中进行:
void EINT1_IRQHandler(void)
{
if(EINT_ReadIntFlag(EINT_LINE_1))
{
/*Clear EINT_LINE0 interrupt flag*/
EINT_ClearIntFlag(EINT_LINE_1);
if(APM_EINT1_flag == 0)
{
if(APM_EINT0_flag == 0)
{
GPIO_Config_T GPIO_ConfigStruct;
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);
/* Config TMR2 GPIO for output PWM */
GPIO_ConfigStruct.pin = GPIO_PIN_5|GPIO_PIN_6;
GPIO_ConfigStruct.mode = GPIO_MODE_AF;
GPIO_ConfigStruct.otype = GPIO_OTYPE_PP;
GPIO_ConfigStruct.speed = GPIO_SPEED_100MHz;
GPIO_Config(GPIOE, &GPIO_ConfigStruct);
TMR_EnableAutoReload(TMR9);
TMR_Enable(TMR9);
APM_EINT1_flag = 1;
printf("APM_LED breathing start \r\n");
}
else
{
printf("Plase stop APM_LED bilinking \r\n");
}
}
else
{
APM_EINT1_flag = 0;
TMR_Disable(TMR9);
GPIO_Config_T GPIO_ConfigStruct;
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);
GPIO_ConfigStruct.pin = GPIO_PIN_5|GPIO_PIN_6;
GPIO_ConfigStruct.mode = GPIO_MODE_OUT;
GPIO_ConfigStruct.otype = GPIO_OTYPE_PP;
GPIO_ConfigStruct.speed = GPIO_SPEED_100MHz;
GPIO_Config(GPIOE, &GPIO_ConfigStruct);
APM_LED2_Off;
APM_LED3_Off;
printf("APM_LED breathing stop \r\n");
}
}
}
定时器中断中加入呼吸灯程序:
if(APM_EINT1_flag == 1)
{
TMR_OCConfig_T OCcongigStruct;
TMR1_breaking_cnt++;
TMR_ConfigOCStructInit(&OCcongigStruct);
OCcongigStruct.mode = TMR_OC_MODE_PWM1;
OCcongigStruct.outputState = TMR_OC_STATE_ENABLE;
OCcongigStruct.polarity = TMR_OC_POLARITY_HIGH;
if(TMR1_breaking_cnt >= 200)
{
TMR1_breaking_cnt = 0;
}
if(TMR1_breaking_cnt<=100)
{
OCcongigStruct.pulse = TMR1_breaking_cnt*10;
}
else if(TMR1_breaking_cnt>100)
{
OCcongigStruct.pulse = (200 - TMR1_breaking_cnt)*10;
}
TMR_ConfigOC1(TMR9, &OCcongigStruct);
TMR_ConfigOC2(TMR9, &OCcongigStruct);
TMR_ConfigOC1Preload(TMR9, TMR_OC_PRELOAD_ENABLE);
TMR_ConfigOC2Preload(TMR9, TMR_OC_PRELOAD_ENABLE);
}
可以看到串口打印基本操作:
实际上咱们得按键采集没有加入防抖,所以会有出现连键的情况,不过和现象还是一致的,效果如下:
|