本帖最后由 秦生0303 于 2022-7-10 20:00 编辑
#申请原创#
在上一贴,很多人留言说点灯被玩坏了,感觉大家对此都不满足,这里还是要再强调一下,那不是点灯,只是一个时钟配置后的一个验证,本人习惯通过操作IO口来验证一下程序正常运行,LED比较直观而已,在最开始是非常发生一些意想不到的错误的。
言归正传,点灯在各位大神的手里已经已经玩出花样了,芯源的测试板板载可控的LED只有两个,想想都可以做什么呢?循环灯、同亮同灭、循环闪烁、再加上一个高级点的呼吸灯。
接下来重点分析一下实现这些功能够需要什么?
循环灯、同亮同灭、循环闪烁基本操作就是IO口的翻转,不过期间需要延时功能,一种是原地等待,一种是定时器定时,这里选择定时器定时,相对来说定时器定时精准;
呼吸灯涉及到的就是定时器的PWM输出了,查阅数据手册发现:
PB8/9可以连接通用定时器(GTIM) ,可以产生输出波形(输出比较和PWM)。 接下来就是模式切换的功能分析,一种是自动模式切换,设计到的就是定时功能,一种是按键操作功能,设计到的就是按键采集,可以使用轮询的方法,也可以使用外部中断的方式,芯源的所有IO口都可以配置为外部中断。 这里我们把灯操作可能设计到的资源需求都分析出来,接下来就让我们一起一步一步实现他们。
对于IO口的数字输出功能不在进行赘述,主要讲一下外部中断功能,GPIO 在设置为数字输入模式时,可作为外部中断信号源,中断的信号源可以设置为高电平、低电平、上升沿、下降沿4 种,结合开发板的原理图,当按键按下时会产生一个下降沿,故选择下降沿方式作为外部中断的触发方式,同时中断数字滤波器可对引脚上的输入信号进行数字滤波,实现防抖功能,注意同一组引脚使用相同的滤波时钟。对于外部中断一般都是使用高优先级,芯片默认的优先级即为高优先级。 初始化代码如下: //******************************************************************************
// 函数名称 : port_EXIT_init
// 函数描述 : 单片机的外部中断初始化
// 输入参数 :
// 参数描述 :
// 输出参数 : 无
// 返回值 : 无
//******************************************************************************
void port_EXIT_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
__RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.IT = GPIO_IT_FALLING;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pins = GPIO_PIN_1|GPIO_PIN_2;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOA,&GPIO_InitStruct);
//配置中断滤波
GPIO_ConfigFilter(CW_GPIOA,bv1,GPIO_FLTCLK_RC150K);
//清除PA00中断标志并使能NVIC
GPIOA_INTFLAG_CLR(bv1| bv2);
NVIC_EnableIRQ(GPIOA_IRQn);
}
接下来就是定时器的初始化配置,需要配置GTIM1的CH3和CH4为PWM输出,GTIM2实现10ms定时功能。 GTIM2代码如下: //******************************************************************************
//* 函数名称 : GTIM2PWM_init
//* 函数描述 : 定时器2的配置为ms级定时器
//* 输入参数 : Period:ms
//* 参数描述 : 对应 定时器引脚初始化及配置
//* 输出参数 : 无
//* 返回值 : 无
//******************************************************************************
void GTIM2_init(uint16_t Period)
{
GTIM_InitTypeDef GTIM_InitStruct;
BTIM_TimeBaseInitTypeDef BTIM_InitStruct;;
__RCC_BTIM_CLK_ENABLE();
__RCC_GTIM2_CLK_ENABLE();
__disable_irq();
NVIC_EnableIRQ(GTIM2_IRQn);
__enable_irq();
GTIM_InitStruct.Mode = GTIM_MODE_COUNTER;
GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV1;
GTIM_InitStruct.ReloadValue = Period-1;//定时ms
GTIM_InitStruct.ToggleOutState = DISABLE;
GTIM_TimeBaseInit(CW_GTIM2, >IM_InitStruct);
GTIM2_ITRConfig(ITR_SOURCE_BTIM1);
BTIM_InitStruct.BTIM_Mode = BTIM_Mode_TIMER;
BTIM_InitStruct.BTIM_OPMode = BTIM_OPMode_Repetitive;
BTIM_InitStruct.BTIM_Period = 999;
BTIM_InitStruct.BTIM_Prescaler = BTIM_PRS_DIV64;
BTIM_TimeBaseInit(CW_BTIM1, &BTIM_InitStruct);
GTIM_ITConfig(CW_GTIM2, GTIM_IT_OV, ENABLE);
GTIM_Cmd(CW_GTIM2, ENABLE);
BTIM_Cmd(CW_BTIM1, ENABLE);
}
每Period ms会产生一个定时中断,首先通过BTIM1进行第一级分频,产生的输出作为GTIM2的输入,这样64M通过BTIM_PRS_DIV64分频后每1ms产生一个输出,同时GTIM2根据设置Period ms产生一个中断。 GTIM1产生PWM初始化代码: //******************************************************************************
//* 函数名称 : GTIM1PWM_init
//* 函数描述 : 定时器1的配置输出PWM
//* 输入参数 :
//* 参数描述 : 对应 定时器引脚初始化及配置
//* 输出参数 : 无
//* 返回值 : 无
//******************************************************************************
void GTIM1PWM_init(uint16_t Period)
{
GPIO_InitTypeDef GPIO_InitStruct;
GTIM_InitTypeDef GTIM_InitStruct;
__RCC_GPIOB_CLK_ENABLE();
__RCC_GTIM1_CLK_ENABLE();
//set PB08 / PB09 as outupt with pur / int = none
GPIO_InitStruct.Pins = GPIO_PIN_8 | GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.IT = GPIO_IT_NONE;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
// PB08_AFx_GTIM1CH3();
// PB09_AFx_GTIM1CH4();
GPIO_Init(CW_GPIOB,&GPIO_InitStruct);
GTIM_InitStruct.Mode = GTIM_MODE_TIME;
GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV64; // DCLK = PCLK / 64 = 64MHz/64 = 1MHz
GTIM_InitStruct.ReloadValue = 1000000/Period-1;//PWM为200Hz (1/200)/(1/1M)
GTIM_InitStruct.ToggleOutState = DISABLE;
GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStruct);
GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL3, GTIM_OC_OUTPUT_PWM_HIGH);
GTIM_SetCompare3(CW_GTIM1, 0);
GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL4, GTIM_OC_OUTPUT_PWM_HIGH);
GTIM_SetCompare4(CW_GTIM1, 0);
// GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE);
// GTIM_Cmd(CW_GTIM1, ENABLE);
}
配置时先不开启定时器及对应IO模式,主要是设置的模式前三种都是IO操作,是普通GPIO功能,而呼吸灯需要配置成复用功能。 定义一个LED的结构体: typedef struct
{
uint8_t Mode_Dis; //LED显示模式
uint8_t Mode_OidDis; //LED上一次显示模式
uint8_t Mode_VIE; //LED控制模式
uint8_t state; //LED过程
uint8_t UpFlag; //更新标志
uint8_t Upcnt; //更新计时计数
}LED_states;
然后通过中断处理函数对参数进行更改,在while中循环检测LED参数,对LED的实际状态进行操作: 外部中断处理(模式切换): if (CW_GPIOA->ISR_f.PIN1)
{
GPIOA_INTFLAG_CLR(bv1);
LED.Mode_Dis++;
LED.Mode_Dis %= 4;
}
定时中断处理: if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_OV))
{
GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_OV);
if(LED.UpFlag == 0)
{
if(LED.Mode_Dis == 0 || LED.Mode_Dis == 1)
{
LED.Upcnt++;
if(LED.Upcnt%100 == 0)
{
LED.Upcnt = 0;
LED.UpFlag = 1;
}
}
else if(LED.Mode_Dis == 2)
{
LED.Upcnt++;
if(LED.Upcnt%100 == 0)
{
LED.Upcnt = 0;
LED.UpFlag = 1;
LED.state++;
LED.state %= 3;
}
}
else if(LED.Mode_Dis == 3)
{
LED.UpFlag = 1;
if(LED.state == 0 && LED.Upcnt < 100)
{
LED.Upcnt++;
}
if(LED.state == 0 && LED.Upcnt == 100)
{
LED.Upcnt--;
LED.state = 1;
}
if(LED.state == 1 && LED.Upcnt > 0)
{
LED.Upcnt--;
}
if(LED.state == 1 && LED.Upcnt == 0)
{
LED.Upcnt++;
LED.state = 0;
}
}
}
}
while中循环检测代码: //******************************************************************************
// 函数名称 : App_LED *
// 函数描述 : LED模式操作 *
// 输入参数 : *
// 参数描述 : 无 *
// 输出参数 : 无 *
// 返回值 : 无 *
//******************************************************************************
void App_LED(void)
{
if(LED.Mode_OidDis != LED.Mode_Dis)
{
LED1Off;
LED2Off;
LED.Upcnt = 0;
LED.state = 0;
LED.UpFlag = 1;
if(LED.Mode_OidDis == 3)
{
GTIM_Cmd(CW_GTIM1, DISABLE);
PB08_AFx_GPIO();
PB09_AFx_GPIO();
}
switch(LED.Mode_Dis)
{
case 0:
LED1On;
LED2On;
break;
case 1:
LED1On;
break;
case 3:
PB08_AFx_GTIM1CH3();
PB09_AFx_GTIM1CH4();
GTIM_Cmd(CW_GTIM1, ENABLE);
break;
default:
break;
}
LED.Mode_OidDis = LED.Mode_Dis;
}
if(LED.UpFlag == 1)
{
if(LED.Mode_Dis == 0 || LED.Mode_Dis == 1)
{
LED1Toggle;
LED2Toggle;
}
else if(LED.Mode_Dis == 2)
{
switch(LED.state)
{
case 0:
LED1On;
break;
case 1:
LED1Off;
LED2On;
break;
case 2:
LED1Off;
LED2Off;
break;
default:
break;
}
}
else if(LED.Mode_Dis == 3)
{
GTIM_SetCompare3(CW_GTIM1, 50*LED.Upcnt);
GTIM_SetCompare4(CW_GTIM1, 50*(100-LED.Upcnt));//注意根据PWM频率修改系数
}
LED.UpFlag = 0;
}
}
就到这吧,二姨家这个编辑器真是不得不吐槽一下,人家是创作时间比较麻烦,这论坛发个贴确实编辑最费时间,不要怪我们不爱发贴,是真不好用。
操作演示
|