首页 » 网站建设 » phpgd库亮度技巧_GD32开拓实战指南第9章 呼吸灯

phpgd库亮度技巧_GD32开拓实战指南第9章 呼吸灯

duote123 2024-11-23 0

扫一扫用手机浏览

文章目录 [+]

MDK:Keil 5.30

开拓板:GD32F207I-EVAL

phpgd库亮度技巧_GD32开拓实战指南第9章 呼吸灯

MCU:GD32F207IK

phpgd库亮度技巧_GD32开拓实战指南第9章 呼吸灯
(图片来自网络侵删)
9.1呼吸灯的事情事理

呼吸灯,便是指灯光设备的亮度随着韶光由暗到亮逐渐增强,再由亮到暗逐渐衰减,很有节奏感地一起一伏,就像是在呼吸一样,因而被广泛运用于手机、电脑等电子设备的指示灯中。

要利用数字器件掌握灯光的强弱,我们很自然就想到 PWM(脉冲宽度调制)技能。
如果以LED 作为灯光设备,且由掌握器输出的 PWM 旗子暗记可以直接驱动 LED,PWM 旗子暗记中的低电平可点亮 LED 灯。
当 LED 以较高的频率进行开关(亮灭)切换时,由于视觉暂留效应,人眼是看不到 LED 灯的闪烁征象的,反响到人眼中能觉得到的是亮度的差别。
即以一定的韶光长度为周期,LED 灯亮的均匀韶光越长,亮度就越高,反之越暗。
因此,我们可以利用高频率的 PWM 旗子暗记,通过调制旗子暗记的占空比,掌握 LED 灯的亮度。

那么详细我们该当掌握 LED 灯以若何的亮度曲线变革能够达到最好的效果呢?亮度随着韶光逐渐变强再衰减,可以用两种常见的数学函数表示,分别是半个周期的正弦函数与指数上升曲线及其对称得到的低落曲线。

图1正弦曲线与指数曲线

相对来说,利用下凹函数曲线灯光处于暗的状态更长,以是指数函数的曲线更符合我们呼吸灯的亮度变革哀求。

9.2呼吸灯实现9.2.1大略办法

笔者先用最大略的办法来实现,也便是定时改变比较寄存器的值。

1.初始化 GPIO

下面剖析详细的定时器配置代码。
本实验利用 PB0 作为定时器 PWM 输出通道,先对它进行初始化。
作 PWM 输出通道的引脚须要被配置为复用推挽输出模式。

/ brief configure PWM GPIO param[in] none param[out] none retval none/static void timer_gpio_init(void){ rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_AF); / Configure PB0 (TIMER2 CH2) as alternate function / gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);}

2.配置定时器模式

在timer2_init()函数中,完成了呼吸灯所须要的定时器 PWM 输出模式配置。

/ brief configure the Breath LED peripheral param[in] none param[out] none retval none /void breath_led_init(void){ / TIMER2 configuration: generate PWM signals with different duty cycles: TIMER2CLK = SystemCoreClock / 120 = 1MHz / timer_oc_parameter_struct timer_ocintpara; timer_parameter_struct timer_initpara; / configure the GPIO ports / timer_gpio_init(); rcu_periph_clock_enable(RCU_TIMER2); timer_deinit(TIMER2); / TIMER1 configuration / timer_initpara.prescaler = 119; timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.counterdirection = TIMER_COUNTER_UP; timer_initpara.period = 250; timer_initpara.clockdivision = TIMER_CKDIV_DIV1; timer_initpara.repetitioncounter = 0; timer_init(TIMER2, &timer_initpara); / CH0 configuration in PWM mode 0 / timer_ocintpara.outputstate = TIMER_CCX_ENABLE; timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE; timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW; timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_ocintpara); / CH0 configuration in PWM mode 0,duty cycle 25% / timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, 0); timer_channel_output_mode_config(TIMER2, TIMER_CH_2, TIMER_OC_MODE_PWM0); timer_channel_output_shadow_config(TIMER2, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE); / auto-reload preload enable / timer_auto_reload_shadow_enable(TIMER2); / TIMER2 enable / timer_enable(TIMER2);}

这个定时器的模式配置紧张分为三个部分,分别为时基初始化,输出模式初始化。

时基初始化

代码中前面的部分是定时器的时基初始化,这部分紧张卖力配置定时器的定时周期、时钟频率、计数办法等。
它利用到库函数timer_init()函数,利用构造体timer_parameter_struct进行配置,该构造体有以下成员:

1) period

定时周期,本色是存储到重载寄存器CAR的数值,脉冲计数器从 0 累加到这个值上溢或从这个值自减至 0 下溢。
这个数值加 1 然后乘以时钟源周期便是实际定时周期。

本实验中向该成员赋值为 255,即定时周期为(255+1) T ,T 为定时器的时钟周期。

2) prescaler

瞄准时器时钟CLK 的预分频值,分频后作为脉冲计数器TIMERx_CNT的驱动时钟,得到脉冲计数器的时钟频率为:CNT=CLK/(N+1),个中 N 为即为赋给本成员的时钟分频值。

本实验给 prescaler 成员赋值为 119,即对时钟 120 分频,以是定时器的时钟周期 T 为 120/120000000。

3) clockdivision

时钟分频因子。
怎么又涌现一个配置时钟分频的呢?要把稳这个clockdivision和上面的 prescaler 是不一样的。
prescaler 预分频配置是对CLK进行分频,分频后的时钟被输出到脉冲计数器CNT。

本实验中是利用内部时钟CLK 作为定时器时钟源的,没有进行滤波以是配置clockdivision为任何数值都没有影响。

4) alignedmode

本成员配置的为脉冲计数器 CNT 的计数模式,分别为向上计数,向下计数,及中心对齐模式。
向上计数即 CNT 从 0 向上累加到 period 中的值,(重载寄存器 CAR 的值),产生上溢事宜;向下计数则 CNT 从period 的值累减至0,产生下溢事宜。
而中心对齐模式则为向上、向下计数的合体,CNT 从 0 累加到period 的值减 1 时,产生一个上溢事宜,然后向下计数到 1 时,产生一个计数器下溢事宜,再从 0 开始重新计数。

输出模式配置

在本函数代码的后面是关于定时器的输出模式配置的。
通用定时器的输出模式由 timer_oc_parameter_struct类型构造体的紧张有以下几个成员:

1) outputstate

配置输出模式的状态使能或关闭输出。

2) outputnstate

本成员的参数值即为比较寄存器 CH2CV的数值,当脉冲计数器CNT与CH2CV的比较结果发生变革时,输出脉冲将发生跳变。

3) ocpolarity

有效电平的极性,把 PWM 模式中的有效电平设置为高电平或低电平。

本实验中向该成员赋值为 TIMER_OC_POLARITY_LOW (有效电平为低电平),由于在上面把输出模式配置为 PWM0 模式,向上计数,以是在 CNT< CH0CV 时,通道 n 输出为低电平,否则为高电平。

4) ocnpolarity

用于比较有效电平的极性。

本实验中便是通过不断改变比较寄存器CH2CV的值,达到掌握 PWM 旗子暗记的占空比呈指数曲线变革的目的。
在本函数代码中,我们对该成员授予初始为 0,而改变比较寄存器 CH0CV 值的操作是在中断做事函数中修正的。
添补完输出模式初始化构造体后,调用输出模式初始化函数 timer_channel_output_config()对通道进行初始化。

以上是最基本的PWM输出调制实现呼吸灯。

笔者接下来还要讲解一下重映射的输出配置。
在这里讲解的是通过重映射 TIMER2_CH2到 PB0 上,由 TIMER2_CH2 输出 PWM 来掌握LED的亮度。
下面我们先容通过库函数来配置该功能的步骤。

1)开启 TIMER2时钟以及复用功能时钟,配置 PB0为复用输出。

要利用 TIMER2,我们必须先开启 TIMER2的时钟,这点相信大家看了这么多代码,该当明白了。
这里我们还要配置 PB0为复用输出,此时,PB0属于复用功能输出。
在此只列出库函数设置 AFIO 时钟的方法。

rcu_periph_clock_enable(RCU_AF);

别的的和前面的配置一样,就不再列出了。

2)初始化 TIMER2,设置 TIMER2的 CAR 和 PSC。

3)设置 TIMER2_CH2 的 PWM 模式,使能 TIMER2的 CH2 输出。

4)使能 TIMER2。

在完成以上设置了之后,我们须要使能 TIMER2。
使能 TIMER2的方法前面已经讲解过:

timer_enable(TIMER2);

5)修正 TIMER2_ CH2CV来掌握占空比。

末了,在经由以上设置之后, PWM 实在已经开始输出了,只是其占空比和频率都是固定的,而我们通过修正 TIMER2_CH2CV则可以掌握 CH2 的输出占空比。
继而掌握LED的亮度。
在库函数中,修正 TIMER2_CH2CV占空比的函数是:

void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t channel, uint32_t pulse)

通过以上5个步骤,我们就可以掌握 TIMER2的 CH2 输出 PWM 波了。

接下来看看主函数的代码:

/ brief main function param[in] none param[out] none retval none/int main(void){ uint16_t i = 0; FlagStatus breathe_flag = SET; //systick init sysTick_init(); / configure the Breath LED peripheral / breath_led_init(); while(1) { / delay a time in milliseconds / delay_ms(5); if(SET == breathe_flag) { i++; } else { i--; } if(250 < i) { breathe_flag = RESET; } if(0 >= i) { breathe_flag = SET; } / configure TIMER channel output pulse value / //timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, i); TIMER_CH2CV(TIMER2) = (uint32_t)i; }}

代码很大略,便是不断改变CH2CV的值从而掌握 CH2 的输出占空比。

9.2.2中断办法

1.天生指数曲线 PWM 数据

要实现 LED 亮度随着指数曲线变革,我们须要利用占空比呈指数曲线变革的 PWM 旗子暗记,而这样的旗子暗记由定时器经由查表产生。
这个表的数据存储在程序中的数组 indexWave中。

uint8_t indexWave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,107,143,191,255,255,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};

这个表有 40 个数字,从图中可以看到这些数字呈指数上升再衰减,恰好是呼吸灯的一个掌握周期。
数字的大小范围是 0~255,即把 LED 的亮度分为了 0~255 个等级。

如果我们把定时器的脉冲计数器 CNT 上限设置为 255,把这个表的数据一个一个地赋值到定时器的比较寄存器CH2CV中,那么在每个 PWM 周期中,当 CNT的计数值小于比较寄存器 CH2CV的值时, 就会在通道中输出低电平,点亮 LED,而随着 CCR 的值由 LED 亮度表得来,以是 LED 点亮的韶光就会呈图中的曲线变革,实现呼吸灯的功能。

这个表的数据是利用 matlab 软件天生的。
该代码运行后会天生一个“index_wave.c”的文件,用户把该文件中的数据复制到工程中的数组中即可。

%本代码用于产生呼吸灯利用的指数函数数据clear;x = [0 : 8/19 : 8]; %设置序列 ,指数上升up = 2.^x ; %求上升指数序列up = uint8(up); %化为8位数据y = [8: -8/19 :0]; %设置序列 ,指数低落down = 2.^y ; %求低落指数序列down = uint8(down); %化为8位数据line = [[0:8/19:8],[8:8/19:16]] %拼接序列val = [up , down] %拼接输出序列dlmwrite('index_wave.c',val); %输出到文件index_wave.cplot(line,val,'.'); %显示波形图

2.初始化 GPIO

这部分和前面的一样,没啥好说的。

3.配置定时器模式

这里也差不多,只是将分频系数设置的轻微大些,其余开启了中断。

/ brief configure the Breath LED peripheral param[in] none param[out] none retval none /void breath_led_init(void){ / TIMER2 configuration: generate PWM signals with different duty cycles/ timer_oc_parameter_struct timer_ocintpara; timer_parameter_struct timer_initpara; / configure the GPIO ports / timer_gpio_init(); rcu_periph_clock_enable(RCU_TIMER2); timer_deinit(TIMER2); / TIMER2 configuration / timer_initpara.prescaler = 3999; timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.counterdirection = TIMER_COUNTER_UP; timer_initpara.period = 255; timer_initpara.clockdivision = TIMER_CKDIV_DIV1; timer_initpara.repetitioncounter = 0; timer_init(TIMER2, &timer_initpara); / CH2 configuration in PWM mode 0 / timer_ocintpara.outputstate = TIMER_CCX_ENABLE; timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE; timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW; timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_ocintpara); / CH2 configuration in PWM mode 0,duty cycle 25% / timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, 0); timer_channel_output_mode_config(TIMER2, TIMER_CH_2, TIMER_OC_MODE_PWM0); timer_channel_output_shadow_config(TIMER2, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE); / auto-reload preload enable / timer_auto_reload_shadow_enable(TIMER2); / Timer2 interrupt setting, preemptive priority 0, sub-priority 2 / nvic_irq_enable(TIMER2_IRQn, 0, 2); / Enable Timer2 update interrupt / timer_interrupt_enable(TIMER2, TIMER_INT_UP); / TIMER2 enable / timer_enable(TIMER2);}

配置好中断,下面就要编写中断做事函数。

/! \brief this function handles TIMER2 exception \param[in] none \param[out] none \retval none/void TIMER2_IRQHandler(void){ static uint8_t pwm_index = 0; //用于PWM查表 static uint8_t period_cnt = 0; //用于打算周期数 if(timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP)) { / 打消TIMER2 中断标志位 / timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP); period_cnt++; if(period_cnt >= 10)//若输出的周期数大于10,输出下一种脉冲宽的PWM波 { //根据PWM表修正定时器的比较寄存器值 TIMER_CH2CV(TIMER2) = indexWave[pwm_index]; pwm_index++; //标志PWM表的下一个元素 //若PWM脉冲表已经输出完成一遍,重置PWM查表标志 if( pwm_index >= 40) { pwm_index=0; } period_cnt=0; //重置周期计数标志 } }}

本中断做事函数在每次定时器更新事宜发生时实行一次(即 256 个定时器时钟周期)。
函数中利用了静态变量 pwm_index 和 period_cnt,它们分别用来查找 PWM 表元素和记录同样占空比的脉冲输出了多少次。

本代码的目的是每 10 次定时器中断更新一次 PWM 表中的数据到比较寄存器中,当遍历完 PWM 表的 40 个元素时,再重头开始遍历 PWM 表,周而复始,重复 LED 的呼吸过程。

全体呼吸过程的韶光打算方法如下:

由于定时器的 prescaler 设置为 3999;

以是定时器的时钟频率:fTIMER = 120000000/(prescaler+1) = 30000 Hz

即定时器的时钟周期为:tTIMER = 1/fTIMER = 1/30000 s

由于定时器的 period 设置为 255;

以是定时器的中断周期为:tint= tTIMER (period+1) =0.00753s

由于 PWM 表有 pwm_index = 40 个亮度占空比数据,同种占空比旗子暗记输出 period_cnt =10 次

以是一个呼吸周期 T = tint 40 10 = 3.41s

9.3呼吸灯的实验征象

将程序编译好下载到板子中,将PF6接到PB0上,可一看到LED1像呼吸一样逐渐变明或者逐渐变暗,但是方法二明显比方法一更流畅,效果更好。

相关文章

微信第三方登录便捷与安全的完美融合

社交平台已成为人们日常生活中不可或缺的一部分。微信作为我国最受欢迎的社交软件之一,拥有庞大的用户群体。为了方便用户在不同平台间切换...

网站建设 2025-02-18 阅读0 评论0

广东高速代码表解码高速公路管理智慧

高速公路作为国家交通动脉,连接着城市与城市,承载着巨大的物流和人流。广东作为我国经济大省,高速公路网络密布,交通流量巨大。为了更好...

网站建设 2025-02-18 阅读0 评论0