芯源半导体CW32
直播中

zealsoft

3年用户 688经验值
擅长:可编程逻辑,嵌入式技术
私信 关注
[经验]

【CW32饭盒派开发板试用体验】使用PWM实现呼吸灯

感谢电子发烧友论坛和武汉芯源所提供的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能够实现呼吸灯的效果。

1.png

3. 编程实现

编程实现可以参考CW32F030_StdPeriph_Lib(固件库)中的Examples\GTIM\gtim_pwm_output示例。但是如前所述,这个例子无法直接在CW32饭盒派中运行,需要进行修改。

程序的基本原理是把GTIM1的CH2通道(PA07)输出周期为500uS,在GTIM1的中断函数内调整占空比递增递减循环改变的PWM信号。

时钟配置

系统时钟由HSI提供,通过PLL倍频到48MHz。

void RCC_Configuration(void)
{
  /* 0. HSI使能并校准 */
  RCC_HSI_Enable(RCC_HSIOSC_DIV6);

  /* 1. 设置HCLK和PCLK的分频系数 */
  RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  
  /* 2. 使能PLL,通过PLL倍频到64MHz */
  RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 8);     // HSI 默认输出频率8MHz
 // RCC_PLL_OUT();  //PC13脚输出PLL时钟
  
  ///< 当使用的时钟源HCLK大于24M,小于等于48MHz:设置FLASH 读等待周期为2 cycle
  ///< 当使用的时钟源HCLK大于48MHz:设置FLASH 读等待周期为3 cycle
  __RCC_FLASH_CLK_ENABLE();
  FLASH_SetLatency(FLASH_Latency_3);   
    
  /* 3. 时钟切换到PLL */
  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;    // DCLK = PCLK / 16 = 48MHz/16 = 3MHz
  GTIM_InitStruct.ReloadValue = Period * 3 - 1;
  GTIM_InitStruct.ToggleOutState = DISABLE; 

  GTIM_TimeBaseInit(CW_GTIM1, &GTIM_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)
{
  /* USER CODE BEGIN */
  // 中断每500us进入一次,每50ms改变一次PosWidth
  static uint16_t TimeCnt = 0;
  GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);
  if (TimeCnt++ >= 100)    // 50ms
  {
    TimeCnt = 0;
    if (Dir)
    {
      PosWidth += 15;    // 5us
    }
    else
    {
      PosWidth -= 15;
    }
    if (PosWidth >= Period * 3)
    {
      Dir = 0;
    }
    if (0 == PosWidth)
    {
      Dir = 1;
    }
    GTIM_SetCompare2(CW_GTIM1, PosWidth);
  }
  /* USER CODE END */
}

主程序

uint32_t Period = 500;    // 周期,单位us
uint32_t PosWidth = 0;    // 正脉宽,单位us
uint8_t Dir = 1;    // 计数方向 1增加,0 减少

int32_t main(void)
{
    /*系统时钟配置 */
    RCC_Configuration();
    /* GPIO配置*/
    GPIO_Configuration();
    PWM_OutputConfig();
    /* NVIC配置*/
    NVIC_Configuration();
    while(1)
    {
        /* 中断服务程序见GTIM1_IRQHandler() */
}

将程序编译下载后,就可以看到LED2呈现呼吸灯的效果。``

更多回帖

发帖
×
20
完善资料,
赚取积分