[CW32F030系列] 【CW32F030CxTx StartKit测评】03点灯并不简单

[复制链接]
1046|3
 楼主| 秦生0303 发表于 2022-7-10 19:52 | 显示全部楼层 |阅读模式
ar, cw
本帖最后由 秦生0303 于 2022-7-10 20:00 编辑

#申请原创#            
      在上一贴,很多人留言说点灯被玩坏了,感觉大家对此都不满足,这里还是要再强调一下,那不是点灯,只是一个时钟配置后的一个验证,本人习惯通过操作IO口来验证一下程序正常运行,LED比较直观而已,在最开始是非常发生一些意想不到的错误的。
      言归正传,点灯在各位大神的手里已经已经玩出花样了,芯源的测试板板载可控的LED只有两个,想想都可以做什么呢?循环灯、同亮同灭、循环闪烁、再加上一个高级点的呼吸灯。
      接下来重点分析一下实现这些功能够需要什么?
      循环灯、同亮同灭、循环闪烁基本操作就是IO口的翻转,不过期间需要延时功能,一种是原地等待,一种是定时器定时,这里选择定时器定时,相对来说定时器定时精准;
      呼吸灯涉及到的就是定时器的PWM输出了,查阅数据手册发现:
IO口.jpg
      PB8/9可以连接通用定时器(GTIM) ,可以产生输出波形(输出比较和PWM)。
      接下来就是模式切换的功能分析,一种是自动模式切换,设计到的就是定时功能,一种是按键操作功能,设计到的就是按键采集,可以使用轮询的方法,也可以使用外部中断的方式,芯源的所有IO口都可以配置为外部中断。
      这里我们把灯操作可能设计到的资源需求都分析出来,接下来就让我们一起一步一步实现他们。

      对于IO口的数字输出功能不在进行赘述,主要讲一下外部中断功能,GPIO 在设置为数字输入模式时,可作为外部中断信号源,中断的信号源可以设置为高电平、低电平、上升沿、下降沿4 种,结合开发板的原理图,当按键按下时会产生一个下降沿,故选择下降沿方式作为外部中断的触发方式,同时中断数字滤波器可对引脚上的输入信号进行数字滤波,实现防抖功能,注意同一组引脚使用相同的滤波时钟。对于外部中断一般都是使用高优先级,芯片默认的优先级即为高优先级。
      初始化代码如下:
  1. //******************************************************************************
  2. // 函数名称  : port_EXIT_init                                            
  3. // 函数描述  : 单片机的外部中断初始化                                       
  4. // 输入参数  :                                                                 
  5. // 参数描述  :                                                                 
  6. // 输出参数  : 无                                                              
  7. // 返回值    : 无                                                              
  8. //******************************************************************************
  9. void port_EXIT_init(void)
  10. {
  11. GPIO_InitTypeDef GPIO_InitStruct;
  12.   __RCC_GPIOA_CLK_ENABLE();
  13.   
  14.   GPIO_InitStruct.IT = GPIO_IT_FALLING;
  15.   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  16.   GPIO_InitStruct.Pins = GPIO_PIN_1|GPIO_PIN_2;
  17.   GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  18.   GPIO_Init(CW_GPIOA,&GPIO_InitStruct);
  19.   
  20.   //配置中断滤波
  21.   GPIO_ConfigFilter(CW_GPIOA,bv1,GPIO_FLTCLK_RC150K);
  22.   
  23.   //清除PA00中断标志并使能NVIC
  24.   GPIOA_INTFLAG_CLR(bv1| bv2);
  25.   NVIC_EnableIRQ(GPIOA_IRQn);

  26. }

      接下来就是定时器的初始化配置,需要配置GTIM1的CH3和CH4为PWM输出,GTIM2实现10ms定时功能。
      GTIM2代码如下:
  1. //******************************************************************************
  2. //* 函数名称  : GTIM2PWM_init
  3. //* 函数描述  : 定时器2的配置为ms级定时器
  4. //* 输入参数  :     Period:ms
  5. //* 参数描述  : 对应    定时器引脚初始化及配置
  6. //* 输出参数  : 无
  7. //* 返回值    : 无
  8. //******************************************************************************
  9. void GTIM2_init(uint16_t Period)
  10. {
  11.   GTIM_InitTypeDef GTIM_InitStruct;
  12.   BTIM_TimeBaseInitTypeDef BTIM_InitStruct;;
  13.   
  14.   __RCC_BTIM_CLK_ENABLE();
  15.   __RCC_GTIM2_CLK_ENABLE();
  16.   
  17.   __disable_irq();
  18.   NVIC_EnableIRQ(GTIM2_IRQn);
  19.   __enable_irq();
  20.   
  21.   GTIM_InitStruct.Mode = GTIM_MODE_COUNTER;
  22.   GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
  23.   GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV1;   
  24.   GTIM_InitStruct.ReloadValue = Period-1;//定时ms  
  25.   GTIM_InitStruct.ToggleOutState = DISABLE;
  26.   GTIM_TimeBaseInit(CW_GTIM2, >IM_InitStruct);
  27.   
  28.   GTIM2_ITRConfig(ITR_SOURCE_BTIM1);
  29.   
  30.   BTIM_InitStruct.BTIM_Mode = BTIM_Mode_TIMER;
  31.   BTIM_InitStruct.BTIM_OPMode = BTIM_OPMode_Repetitive;
  32.   BTIM_InitStruct.BTIM_Period = 999;
  33.   BTIM_InitStruct.BTIM_Prescaler = BTIM_PRS_DIV64;
  34.   BTIM_TimeBaseInit(CW_BTIM1, &BTIM_InitStruct);
  35.   
  36.   GTIM_ITConfig(CW_GTIM2, GTIM_IT_OV, ENABLE);
  37.   GTIM_Cmd(CW_GTIM2, ENABLE);
  38.   BTIM_Cmd(CW_BTIM1, ENABLE);

  39. }
      每Period ms会产生一个定时中断,首先通过BTIM1进行第一级分频,产生的输出作为GTIM2的输入,这样64M通过BTIM_PRS_DIV64分频后每1ms产生一个输出,同时GTIM2根据设置Period ms产生一个中断。
      GTIM1产生PWM初始化代码:
  1. //******************************************************************************
  2. //* 函数名称  : GTIM1PWM_init
  3. //* 函数描述  : 定时器1的配置输出PWM
  4. //* 输入参数  :
  5. //* 参数描述  : 对应    定时器引脚初始化及配置
  6. //* 输出参数  : 无
  7. //* 返回值    : 无
  8. //******************************************************************************
  9. void GTIM1PWM_init(uint16_t Period)
  10. {
  11.   
  12.   GPIO_InitTypeDef GPIO_InitStruct;
  13.   GTIM_InitTypeDef GTIM_InitStruct;
  14.   
  15.   __RCC_GPIOB_CLK_ENABLE();
  16.   __RCC_GTIM1_CLK_ENABLE();

  17.   //set PB08 / PB09 as outupt with pur / int = none
  18.   GPIO_InitStruct.Pins  = GPIO_PIN_8 | GPIO_PIN_9;
  19.   GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
  20.   GPIO_InitStruct.IT    = GPIO_IT_NONE;
  21.   GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  22. //  PB08_AFx_GTIM1CH3();
  23. //  PB09_AFx_GTIM1CH4();
  24.   GPIO_Init(CW_GPIOB,&GPIO_InitStruct);
  25.   
  26.   GTIM_InitStruct.Mode = GTIM_MODE_TIME;
  27.   GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
  28.   GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV64;    // DCLK = PCLK / 64 = 64MHz/64 = 1MHz
  29.   GTIM_InitStruct.ReloadValue = 1000000/Period-1;//PWM为200Hz  (1/200)/(1/1M)
  30.   GTIM_InitStruct.ToggleOutState = DISABLE;
  31.   
  32.   GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStruct);
  33.   GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL3, GTIM_OC_OUTPUT_PWM_HIGH);
  34.   GTIM_SetCompare3(CW_GTIM1, 0);
  35.   GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL4, GTIM_OC_OUTPUT_PWM_HIGH);
  36.   GTIM_SetCompare4(CW_GTIM1, 0);
  37. //  GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE);

  38. //  GTIM_Cmd(CW_GTIM1, ENABLE);

  39. }
      配置时先不开启定时器及对应IO模式,主要是设置的模式前三种都是IO操作,是普通GPIO功能,而呼吸灯需要配置成复用功能。
      定义一个LED的结构体:
  1. typedef struct
  2. {
  3. uint8_t Mode_Dis; //LED显示模式
  4. uint8_t Mode_OidDis; //LED上一次显示模式
  5. uint8_t Mode_VIE; //LED控制模式
  6. uint8_t state; //LED过程
  7. uint8_t UpFlag; //更新标志
  8. uint8_t Upcnt; //更新计时计数
  9. }LED_states;
      然后通过中断处理函数对参数进行更改,在while中循环检测LED参数,对LED的实际状态进行操作:
      外部中断处理(模式切换):
  1. if (CW_GPIOA->ISR_f.PIN1)
  2.   {
  3.     GPIOA_INTFLAG_CLR(bv1);
  4.     LED.Mode_Dis++;
  5.     LED.Mode_Dis %= 4;
  6.   }
      定时中断处理:
  1. if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_OV))
  2.   {
  3.   GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_OV);
  4.   if(LED.UpFlag == 0)
  5.   {
  6.     if(LED.Mode_Dis == 0 || LED.Mode_Dis == 1)
  7.     {
  8.       LED.Upcnt++;
  9.       if(LED.Upcnt%100 == 0)
  10.       {
  11.         LED.Upcnt = 0;
  12.         LED.UpFlag = 1;
  13.        }
  14.      }
  15.   else if(LED.Mode_Dis == 2)
  16.   {
  17.      LED.Upcnt++;
  18.      if(LED.Upcnt%100 == 0)
  19.     {
  20.        LED.Upcnt = 0;
  21.        LED.UpFlag = 1;
  22.        LED.state++;
  23.        LED.state %= 3;
  24.     }
  25.   }
  26.   else if(LED.Mode_Dis == 3)
  27.   {
  28.     LED.UpFlag = 1;
  29.     if(LED.state == 0 && LED.Upcnt < 100)
  30.     {
  31.       LED.Upcnt++;
  32.     }
  33.     if(LED.state == 0 && LED.Upcnt == 100)
  34.     {
  35.       LED.Upcnt--;
  36.       LED.state = 1;
  37.     }
  38.     if(LED.state == 1 && LED.Upcnt > 0)
  39.     {
  40.      LED.Upcnt--;
  41.     }
  42.    if(LED.state == 1 && LED.Upcnt == 0)
  43.    {
  44.       LED.Upcnt++;
  45.       LED.state = 0;
  46.    }
  47.       }
  48.     }
  49.   }
      while中循环检测代码:
  1. //******************************************************************************
  2. // 函数名称  : App_LED                                                     *
  3. // 函数描述  : LED模式操作                                                *
  4. // 输入参数  :                                                                 *
  5. // 参数描述  : 无                                                              *
  6. // 输出参数  : 无                                                              *
  7. // 返回值    : 无                                                              *
  8. //******************************************************************************
  9. void App_LED(void)
  10. {
  11.   if(LED.Mode_OidDis != LED.Mode_Dis)
  12.   {
  13.     LED1Off;
  14.     LED2Off;
  15.     LED.Upcnt = 0;
  16.     LED.state = 0;
  17.     LED.UpFlag = 1;
  18.    
  19.     if(LED.Mode_OidDis == 3)
  20.     {
  21.       GTIM_Cmd(CW_GTIM1, DISABLE);
  22.       PB08_AFx_GPIO();
  23.       PB09_AFx_GPIO();
  24.       
  25.     }
  26.    
  27.     switch(LED.Mode_Dis)
  28.     {
  29.       case 0:
  30.         LED1On;
  31.         LED2On;
  32.         break;
  33.         
  34.       case 1:
  35.         LED1On;
  36.         break;
  37.         
  38.       case 3:
  39.         PB08_AFx_GTIM1CH3();
  40.         PB09_AFx_GTIM1CH4();
  41.         GTIM_Cmd(CW_GTIM1, ENABLE);
  42.         break;

  43.       default:
  44.         break;
  45.     }
  46.     LED.Mode_OidDis = LED.Mode_Dis;
  47.   }
  48.   
  49.   if(LED.UpFlag == 1)
  50.   {
  51.     if(LED.Mode_Dis == 0 || LED.Mode_Dis == 1)
  52.     {
  53.       LED1Toggle;
  54.       LED2Toggle;
  55.     }
  56.     else if(LED.Mode_Dis == 2)
  57.     {
  58.       switch(LED.state)
  59.       {
  60.         case 0:
  61.           LED1On;
  62.           break;
  63.          
  64.         case 1:
  65.           LED1Off;
  66.           LED2On;
  67.           break;
  68.          
  69.         case 2:
  70.           LED1Off;
  71.           LED2Off;
  72.           break;
  73.          
  74.         default:
  75.           break;
  76.       }
  77.     }
  78.     else if(LED.Mode_Dis == 3)
  79.     {
  80.       GTIM_SetCompare3(CW_GTIM1, 50*LED.Upcnt);
  81.       GTIM_SetCompare4(CW_GTIM1, 50*(100-LED.Upcnt));//注意根据PWM频率修改系数
  82.     }
  83.    
  84.     LED.UpFlag = 0;
  85.   }

  86. }
      就到这吧,二姨家这个编辑器真是不得不吐槽一下,人家是创作时间比较麻烦,这论坛发个贴确实编辑最费时间,不要怪我们不爱发贴,是真不好用。

操作演示





 楼主| 秦生0303 发表于 2022-7-10 19:54 | 显示全部楼层
如何插入视频呢?求大神指教
guijial511 发表于 2022-7-11 08:14 来自手机 | 显示全部楼层
二姨的编辑器确实有点不好用啊!
huquanz711 发表于 2022-7-15 07:50 来自手机 | 显示全部楼层
确实不简单…………
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

181

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部