感谢电子发烧友论坛和武汉芯源所提供的CW32饭盒派开发板的测试机会。
武汉芯源提供了CW32F030芯片的固件库(https://www.whxy.com/support/filelist?id=13&p=1),其中包括丰富的示例,涵盖了芯片的各种功能。但是这些例子都不是针对CW32饭盒派开发板的,不能直接使用,今天就介绍如何修改其中的一个示例,来借助PWM功能实现呼吸灯。
1. PWM
PWM是脉冲宽度调制(Pulse Width Modulation)的缩写,简称脉宽调制,这种方式能使电源的输出电压在工作条件变化时保持恒定,是利用微处理器的数字信号对模拟电路进行控制的一种非常有效的技术,常见应用电机调速,照明灯调光等。
CW32F030芯片的GTIM定时器的4个通道都可以独立产生PWM。PWM模式可以产生一个由重载寄存器 GTIMx_ARR 确定频率、由比较捕获寄存器 GTIMx_CCRy 确定占空比的信号。向GTIM_CCMR 寄存器中的 CCyM 位写入 0xE 或 0xF,能够独立地设置每个CHy 输出通道产生一路 PWM。设置GTIMx_CMMR.CCyM 为 0xE,当
GTIMx_CNT >= GTIMx_CCRy 时,CHy 通道输出高电平,否则输出低电平。设置 GTIMx_CMMR.CCyM 为 0xF,当 GTIMx_CNT < GTIMx_CCRy 时,CHy 通道输出高电平,否则输出低电平。
2. 呼吸灯的工作原理
呼吸灯是一种常见的LED灯光效果,其名称来源于灯光呼吸般的渐变亮度变化。上电后LED灯渐渐变亮,当达到最亮时保持几秒钟,然后渐渐变暗直到熄灭。熄灭几秒钟后又渐渐由暗变亮,这样一直循环下去。这种效果可以通过PWM来实现,通过调节PWM的占空比可以实现亮暗不同程度的变化。
下图是CW32饭盒派底板原理图,从中可以看到三个LED灯对应的GPIO为PA7、PA8和PC13,而查阅CW32F030芯片的GPIO
复用功能分配表发现只有PA7和GTIM1的通道2对应,其他两个GPIO都没有对应的GTIM定时器通道,所以只有LED2能够实现呼吸灯的效果。
3. 编程实现
编程实现可以参考CW32F030_StdPeriph_Lib(固件库)中的Examples\GTIM\gtim_pwm_output示例。但是如前所述,这个例子无法直接在CW32饭盒派中运行,需要进行修改。
程序的基本原理是把GTIM1的CH2通道(PA07)输出周期为500uS,在GTIM1的中断函数内调整占空比递增递减循环改变的PWM信号。
时钟配置
系统时钟由HSI提供,通过PLL倍频到48MHz。
void RCC_Configuration(void)
{
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 8);
__RCC_FLASH_CLK_ENABLE();
FLASH_SetLatency(FLASH_Latency_3);
RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
RCC_SystemCoreClockUpdate(64000000);
}
GPIO配置
设置PA07的功能为GTIM1的CH2,同时设置其为输出。
void GPIO_Configuration(void)
{
__RCC_GPIOA_CLK_ENABLE();
PA07_AFx_GTIM1CH2();
PA07_DIGTAL_ENABLE();
PA07_DIR_OUTPUT();
PA07_PUSHPULL_ENABLE();
PA07_SPEED_HIGH();
}
PWM定时器配置
GTIM1经16分频后,以3MHz的频率计数,ARR设置为1499,GTIM1的溢出周期为500us,即GTIM1每500us进入一次中断。
void PWM_OutputConfig(void)
{
GTIM_InitTypeDef GTIM_InitStruct;
__RCC_GTIM1_CLK_ENABLE();
GTIM_InitStruct.Mode = GTIM_MODE_TIME;
GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV16;
GTIM_InitStruct.ReloadValue = Period * 3 - 1;
GTIM_InitStruct.ToggleOutState = DISABLE;
GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStruct);
GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL2, GTIM_OC_OUTPUT_PWM_HIGH);
GTIM_SetCompare2(CW_GTIM1, PosWidth);
GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE);
GTIM_Cmd(CW_GTIM1, ENABLE);
}
中断使能
void NVIC_Configuration(void)
{
__disable_irq();
NVIC_EnableIRQ(GTIM1_IRQn);
__enable_irq();
}
中断服务函数
GTIM1每500us进入一次中断,每50ms改变一次CH2的CCR寄存器的值,即改变PWM的正脉宽,步长为5us,先递增到ARR,然后递减到0,如此反复。
void GTIM1_IRQHandler(void)
{
static uint16_t TimeCnt = 0;
GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);
if (TimeCnt++ >= 100)
{
TimeCnt = 0;
if (Dir)
{
PosWidth += 15;
}
else
{
PosWidth -= 15;
}
if (PosWidth >= Period * 3)
{
Dir = 0;
}
if (0 == PosWidth)
{
Dir = 1;
}
GTIM_SetCompare2(CW_GTIM1, PosWidth);
}
}
主程序
uint32_t Period = 500;
uint32_t PosWidth = 0;
uint8_t Dir = 1;
int32_t main(void)
{
RCC_Configuration();
GPIO_Configuration();
PWM_OutputConfig();
NVIC_Configuration();
while(1)
{
}
将程序编译下载后,就可以看到LED2呈现呼吸灯的效果。``